/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *   
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/* $Rev$ $Date$ */

#ifndef _REFCOUNTINGPOINTER_H
#define _REFCOUNTINGPOINTER_H


#include "commonj/sdo/export.h"
#include "commonj/sdo/SDORuntimeException.h"

#include <iostream>

namespace commonj{
namespace sdo{

/** 
 * RefcountingPointer is a template for all SDO pointers.
 * The ref count increases with each use, and drops when one of these
 * goes out of scope.
 * Refcounting pointers are a lot like smart pointers, however in this
 * implementation there is a cast method to a void*, so unlike real
 * smart pointers, a user might call "delete mypointer", which would
 * compile, but cause a crash. 
 * RefCountingPointers do not need to be deleted.
 */

template <class T>
class RefCountingPointer {
        // This private typedef is an offset pointer to a member function. 
        // It helps us with a trick for safe conversions to bool
        typedef void (RefCountingPointer<T>::*UnspecifiedBoolType)() const;

    public:
        typedef T* PointerType;

        /*SDO_API*/ RefCountingPointer(PointerType realPtr = 0);
        /*SDO_API*/ RefCountingPointer(const RefCountingPointer& rhs);
        template <typename otherType>
        RefCountingPointer(const RefCountingPointer<otherType>& rhs) 
        : pointee(getRawPointer(rhs))
        {
            init();
        }
        /*SDO_API*/ ~RefCountingPointer();
        /*SDO_API*/ RefCountingPointer& operator=(const RefCountingPointer& rhs);
        /*SDO_API*/ T* operator->() const;
        /*SDO_API*/ T& operator*() const;
        /*SDO_API*/ bool operator!() const;

        // Returns an unspecified boolean type to allow for testing if the
        // associated pointer is null. The boolean type is not specified in order
        // to avoid unintended implicit conversions.
        operator typename RefCountingPointer<T>::UnspecifiedBoolType() const
        {
          if (pointee) {
            return &RefCountingPointer<T>::UnspecifiedBoolHelper;
          }
          return 0;
        }

        template <class otherType>
        operator RefCountingPointer<otherType>()
        {
            return RefCountingPointer<otherType>(pointee);
        }

        template <class otherType>
        operator const RefCountingPointer<otherType>() const
        {
            return RefCountingPointer<otherType>(pointee);
        }

        friend std::ostream& operator<< (std::ostream &os, const RefCountingPointer<T>& ptr)
        {
            if (!ptr)
            {
                os << "RefCountingPointer is NULL" << std::endl;
            }
            else
            {
                ptr->printSelf(os);
            }

            return os;
        }

        // utility function for accessing the underlying raw pointer
        template <class otherType>
        friend otherType* getRawPointer(const RefCountingPointer<otherType>& pointer);

    private:
        // Defines a member function we can return the addresss to when
        // evaluating whether the pointer is valid or not. This function
        // serves no purpose beyond providing a non-zero value for use in
        // boolean expressions.
        void UnspecifiedBoolHelper() const {}

        PointerType pointee;
        void init();
};



template<class T>
void RefCountingPointer<T>::init()
{
    if (pointee == 0) return;
    pointee->addRef();
}

template<class T>
/*SDO_API*/ RefCountingPointer<T>::RefCountingPointer(typename RefCountingPointer<T>::PointerType realPtr)
:pointee(realPtr)
{
    init();
}

template<class T>
/*SDO_API*/ RefCountingPointer<T>::RefCountingPointer(const RefCountingPointer& rhs)
: pointee(rhs.pointee)
{
    init();
}

template<class T>
/*SDO_API*/ RefCountingPointer<T>::~RefCountingPointer()
{
    if (pointee)pointee->releaseRef();
}

template<class T>
/*SDO_API*/ RefCountingPointer<T>& RefCountingPointer<T>::operator=(const RefCountingPointer& rhs)
{
    if (pointee != rhs.pointee)
    {
        T *oldP = pointee;
        pointee = rhs.pointee;
        init();
        if (oldP) oldP->releaseRef();
    }
    return *this;
}

template<class T>
/*SDO_API*/ bool RefCountingPointer<T>::operator!() const
{
    return (pointee == 0);
}

template<class T>
/*SDO_API*/ T* RefCountingPointer<T>::operator->() const 
{
    if (pointee == 0)
      throw SDONullPointerException(TUSCANY_SDO_EINFO, 
                                    "");
    return pointee;
}

template<class T>
/*SDO_API*/ T& RefCountingPointer<T>::operator*() const
{
    return *pointee;
}

template <class otherType>
otherType* getRawPointer(const RefCountingPointer<otherType>& pointer) 
{
  return pointer.pointee;
}

template <class T, class otherType>
T dynamicCast(const RefCountingPointer<otherType>& pointer)
{
  return dynamic_cast<typename T::PointerType>(getRawPointer(pointer));
}

template <class T, class otherType>
T staticCast(const RefCountingPointer<otherType>& pointer)
{
  return static_cast<typename T::PointerType>(getRawPointer(pointer));
}

template <class T, class otherType>
bool operator==(const RefCountingPointer<T>& first, const RefCountingPointer<otherType>& second)
{
  return (getRawPointer(first) == getRawPointer(second));
}

template <class T, class otherType>
bool operator==(const RefCountingPointer<T>& first, const otherType* second)
{
  return (getRawPointer(first) == second);
}

template <class T, class otherType>
bool operator==(const T* first, const RefCountingPointer<otherType>& second)
{
  return (first == getRawPointer(second));
}

template <class T, class otherType>
bool operator!=(const RefCountingPointer<T>& first, const RefCountingPointer<otherType>& second)
{
  return !(first == second);
}

template <class T, class otherType>
bool operator!=(const RefCountingPointer<T>& first, const otherType* second)
{
  return !(first == second);
}

template <class T, class otherType>
bool operator!=(const T* first, const RefCountingPointer<otherType>& second)
{
  return !(first == second);
}

template <class T, class otherType>
bool operator<(const RefCountingPointer<T>& first, const RefCountingPointer<otherType>& second)
{
  return (getRawPointer(first) < getRawPointer(second));
}

template <class T, class otherType>
bool operator>(const RefCountingPointer<T>& first, const RefCountingPointer<otherType>& second)
{
  return (second < first);
}

class DataObject;
typedef RefCountingPointer<DataObject> DataObjectPtr;
class DataObjectImpl;
typedef RefCountingPointer<DataObjectImpl> DataObjectImplPtr;
class Property;
typedef RefCountingPointer<Property> PropertyPtr;
class PropertyImpl;
typedef RefCountingPointer<PropertyImpl> PropertyImplPtr;
class DataGraph;
typedef RefCountingPointer<DataGraph> DataGraphPtr;
class DataFactory;
typedef RefCountingPointer<DataFactory> DataFactoryPtr;
class DataFactoryImpl;
typedef RefCountingPointer<DataFactoryImpl> DataFactoryImplPtr;
class Sequence;
typedef RefCountingPointer<Sequence> SequencePtr;
class SequenceImpl;
typedef RefCountingPointer<SequenceImpl> SequenceImplPtr;
class ChangeSummary;
typedef RefCountingPointer<ChangeSummary> ChangeSummaryPtr;
class ChangeSummaryImpl;
typedef RefCountingPointer<ChangeSummaryImpl> ChangeSummaryImplPtr;
class XMLDocument;
typedef RefCountingPointer<XMLDocument> XMLDocumentPtr;
class XSDHelper;
typedef RefCountingPointer<XSDHelper> XSDHelperPtr;
class XSDHelperImpl;
typedef RefCountingPointer<XSDHelperImpl> XSDHelperImplPtr;
class XMLHelper;
typedef RefCountingPointer<XMLHelper> XMLHelperPtr;

}
}

#endif
