/*
 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// adornment - generic attached-storage facility
//
// Adornments are dynamic objects (subclasses of class Adornment) that can
// be "attached" ("hung off") any object derived from Adornable. Any number
// of Adornments can be attached to one object using different unique keys
// (of type void *).
//
// Adornments can be used by a single caller to remember data "with" an Adornable
// object. Multiple, cooperating callers can share an Adornment as long as they
// agree on the Key.
//
// Memory management: All Adornments must be dynamically allocated, and will be
// deleted when their Adornable dies. Once attached, their memory is owned by the
// Adornable (NOT the caller). Do not get fancy with an Adornment's memory;
// trying to share one Adornment instance between Adornables or slots is bad.
// If you need shared storage, use a RefPointer attachment.
//
// Your Adornment's destructor will be called when its Adornable dies, or when
// its slot is replaced (whichever happens sooner). So you CAN get notification
// of an object's death by attaching an Adornment with a unique key and putting
// code in its destructor.
//
// It is fairly popular for a subclass of Adornable to rename its getAdornment and
// adornment methods as operator [], but we won't make that decision for you
// at this level.
//
#ifndef _H_ADORNMENTS
#define _H_ADORNMENTS

#include <security_utilities/utilities.h>
#include <security_utilities/threading.h>
#include <map>


namespace Security {

class Adornable;


//
// An Adornment is data "hung" (stored with) an Adornable.
//
class Adornment {
	friend class Adornable;
public:
	typedef const void *Key;
	
	virtual ~Adornment() = 0;
	
protected:
	Adornment() { }
};


//
// An Adornable can carry Adornments, potentially a different one for each
// Key. We provide both a raw interface (dealing in Adornment subclasses),
// and an attachment form that just pretends that the Adornable has extra,
// dynamically allocated members filed under various keys.
//
class Adornable {
public:
	Adornable() : mAdornments(NULL) { }
	~Adornable();
	
	// adornment keys (slots)
	typedef Adornment::Key Key;
	
	// primitive access, raw form
	Adornment *getAdornment(Key key) const;				// NULL if not present
	void setAdornment(Key key, Adornment *ad);			// use NULL to delete
	Adornment *swapAdornment(Key key, Adornment *ad);	// rotate in/out
	
	// typed primitive access. Ad must be a unique subclass of Adornment
	template <class Ad>
	Ad *getAdornment(Key key) const
	{ return safe_cast<Ad *>(getAdornment(key)); }

	template <class Ad>
	Ad *swapAdornment(Key key, Ad *ad)
	{ return safe_cast<Ad *>(swapAdornment(key, ad)); }
	
	// inquiries for the Adornable itself
	bool empty() const				{ return !mAdornments || mAdornments->empty(); }
	unsigned int size() const		{ return mAdornments ? mAdornments->size() : 0; }
	void clearAdornments();
	
public:
	// Adornment ref interface.  Will return an (optionally constructed) Adornment &.
	template <class T> T &adornment(Key key);
	template <class T, class Arg1> T &adornment(Key key, Arg1 &arg1);
	template <class T, class Arg1, class Arg2> T &adornment(Key key, Arg1 &arg1, Arg2 &arg2);
	template <class T, class Arg1, class Arg2, class Arg3> T &adornment(Key key, Arg1 &arg1, Arg2 &arg2, Arg3 &arg3);

	// attached-value interface
	template <class T> T &attachment(Key key);
	template <class T, class Arg1> T &attachment(Key key, Arg1 arg1);

private:
	Adornment *&adornmentSlot(Key key);

	template <class Type>
	struct Attachment : public Adornment {
		Attachment() { }
		template <class Arg1> Attachment(Arg1 arg) : mValue(arg) { }
		Type mValue;
	};

private:
	typedef std::map<Key, Adornment *> AdornmentMap;
	AdornmentMap *mAdornments;
};


//
// Out-of-line implementations
//
template <class T> T &
Adornable::adornment(Key key)
{
	Adornment *&slot = adornmentSlot(key);
	if (!slot)
		slot = new T;
	return dynamic_cast<T &>(*slot);
}

template <class T, class Arg1> T &
Adornable::adornment(Key key, Arg1 &arg1)
{
	Adornment *&slot = adornmentSlot(key);
	if (!slot)
		slot = new T(arg1);
	return dynamic_cast<T &>(*slot);
}

template <class T, class Arg1, class Arg2> T &
Adornable::adornment(Key key, Arg1 &arg1, Arg2 &arg2)
{
	Adornment *&slot = adornmentSlot(key);
	if (!slot)
		slot = new T(arg1, arg2);
	return dynamic_cast<T &>(*slot);
}

template <class T, class Arg1, class Arg2, class Arg3> T &
Adornable::adornment(Key key, Arg1 &arg1, Arg2 &arg2, Arg3 &arg3)
{
	Adornment *&slot = adornmentSlot(key);
	if (!slot)
		slot = new T(arg1, arg2, arg3);
	return dynamic_cast<T &>(*slot);
}

template <class T>
T &Adornable::attachment(Key key)
{
	typedef Attachment<T> Attach;
	Adornment *&slot = adornmentSlot(key);
	if (!slot)
		slot = new Attach;
	return safe_cast<Attach *>(slot)->mValue;
}

template <class T, class Arg1>
T &Adornable::attachment(Key key, Arg1 arg1)
{
	typedef Attachment<T> Attach;
	Adornment *&slot = adornmentSlot(key);
	if (!slot)
		slot = new Attach(arg1);
	return safe_cast<Attach *>(slot)->mValue;
}


}	// end namespace Security

#endif //_H_ADORNMENTS
