#ifndef DECORATOR_H_INCLUDED
#define DECORATOR_H_INCLUDED

#include <string>

// --------------------------------------------------------------------------

/**
 * @addtogroup interfaces Interfeisai
 * @{
 */

class Decorator;

/**
 * Abstraktus dokumentas.  Galima jį peržiūrinėti, spausdinti, redaguoti.
 *
 * Taip pat pateikiamos funkcijos darbui su dekoratoriais.
 */
class Document {
public:

    /** Konstruktorius. */
    Document() {}

    /** Virtualus destruktorius. */
    virtual ~Document() = 0;

    // Darbas su dokumentais

    /** Galime dokumentą peržiūrėti. */
    virtual void view() = 0;

    /** Galime dokumentą spausdinti. */
    virtual void print() = 0;

    /** Galime dokumentą redaguoti. */
    virtual void edit() = 0;

    // Darbas su dekoratoriais

    /**
     * Grąžina nurodyto tipo dekoratorių.
     *
     * Jei dekoratorių grandinėje yra daugiau nei vienas nurodyto tipo
     * dekoratorius, gražina išoriškiausią.  Jei nurodyto tipo dekoratoriaus
     * nėra, grąžina 0.
     *
     * Taisyklės primena dynamic_cast<>, t.y., jei objektas yra dekoruotas ne
     * pačios DecoratorType klasės, bet iš jos išvestos poklasės dekoratoriumi,
     * jis bus grąžinamas.
     *
     * Jei norite rasti visus nurodyto tipo dekoratorius, darykite šitaip:
     *
     * <pre>
     *     for (Document* tmp = document;
     *          DecoratorType* d = tmp->decorator<DecoratorType>();
     *          tmp = d->next())
     *     {
     *         // do something with d
     *     }
     * </pre>
     */
    template<typename DecoratorType> DecoratorType* decorator();

    /**
     * Grąžina dokumentą be nurodyto tipo dekoratoriaus.
     *
     * Parametras all sako, ar reikia išmesti visus nurodyto tipo dekoratorius
     * (jei true), ar tik išoriškiausią (jei false).
     *
     * Išmesti dekoratoriai (jei tokių buvo) sunaikinami.
     *
     * Taisyklės primena dynamic_cast<>, t.y., jei objektas yra dekoruotas ne
     * pačios DecoratorType klasės, bet iš jos išvestos poklasės dekoratoriumi,
     * jis bus išmetamas.
     */
    template<typename DecoratorType> Document* unwrap(bool all);

    /**
     * Grąžina kitą objektą dekoratorių grandinėje.  Jei objektas nedekoruotas,
     * grąžina 0.
     */
    virtual Document* next() const { return 0; }

private:

    /**
     * Nustato kitą objektą dekoratorių grandinėje.
     *
     * Funkcija reikalinga norint realizuoti dekoratoriaus išmetimą iš
     * sąrašo unwrap metode.
     */
    virtual void setNext(Document* next) {}

};

/// @}

template<typename DecoratorType>
DecoratorType* Document::decorator()
{
    if (DecoratorType* p = dynamic_cast<DecoratorType*>(this))
	return p;
    else if (Document * n = next())
	return n->decorator<DecoratorType>();
    else
	return 0;
}

template<typename DecoratorType>
Document* Document::unwrap(bool all)
{
    if (dynamic_cast<DecoratorType*>(this)) {
	Document * n = next();
	setNext(0);
	delete this;
	return n;
    } else {
	setNext(next()->unwrap<DecoratorType>(all));
	return this;
    }
}

/// @{

/**
 * Abstraktus dekoratorius.
 */
class Decorator : public Document {

    Document* next_;		///< Dekoruotas objektas

public:

    /**
     * Konstruktorius.
     *
     * Dekoruotas objektas @a next gali būti 0, bet tik tuo atveju, jei
     * dekoratorių naudosite kaip parametrą unwrap arba decorator funkcijoms ir
     * niekam daugiau.
     */
    Decorator(Document* next)
	: next_(next) {}

    /** Destruktorius.  Taip pat sunaikina ir dekoruotą objektą. */
    ~Decorator() = 0;

    // Darbas su dokumentu
    void view() { next_->view(); }
    void print() { next_->print(); }
    void edit() { next_->edit(); }

    // Darbas su dekoratoriais
    Document* next() const { return next_; }

private:

    void setNext(Document* next) { next_ = next; }

};

/// @}

// --------------------------------------------------------------------------

/**
 * @addtogroup documents Konkretūs dokumentai
 * @{
 */

/**
 * Tekstinis dokumentas.
 */
class TextDocument : public Document {
public:

    // Darbas su dokumentais
    void view();
    void print();
    void edit();

};

/**
 * Paveiksliukas.
 */
class Image : public Document {
public:

    // Darbas su dokumentais
    void view();
    void print();
    void edit();

};

/// @}

// --------------------------------------------------------------------------

/**
 * @addtogroup decorators Konkretūs dekoratoriai
 * @{
 */

/**
 * Nemodifikuojamas dokumentas.
 */
class ReadOnly : public Decorator
{
public:

    explicit ReadOnly(Document* next)
	: Decorator(next) {}

    void edit();

};

/**
 * Anotacija skaitytojui.
 */
class Annotation : public Decorator
{
    std::string annotation_;

public:

    Annotation(Document* next, const std::string& annotation)
	: Decorator(next), annotation_(annotation) {}

    void view();
    void print();

    // Papildomas funkcionalumas

    /** Grąžina anotaciją. */
    const std::string& annotation() const
    { return annotation_; }

    /** Pakeičia anotaciją. */
    void changeAnnotation(const std::string& annotation)
    { annotation_ = annotation; }

};

/// @}

// --------------------------------------------------------------------------

#endif


syntax highlighted by Code2HTML, v. 0.9.1