// $Id: MetaObject.java,v 1.24 1998/06/08 10:14:13 oliva Exp $

/* Copyright 1997,1998 Alexandre Oliva <oliva@dcc.unicamp.br>
 *
 * This file 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.
 */

package BR.unicamp.Guarana;

/** A MetaObject is an Object responsible for implementing meta-level
    behavior.  A single MetaObject will be directly associated with an
    Object.  However, by using Composers, several orthogonal
    MetaObjects can be may be associated with a single base-level
    Object.

    <p>A MetaObject receives intercepted Operations targeted to a
    base-level Object it is associated with.  It takes whatever
    meta-level actions it finds appropriate and finally returns the
    Result of the Operation.  These actions are split in three passes:

    <ul>

    <li>First, it analyses the requested Operation.  It may either
    provide a Result for the Operation, a replacement Operation to be
    performed or a request for the Operation to be performed.  If it
    does not provide a Result itself, it may ask to be presented the
    Result after the Operation is performed.</li>

    <li>If a Result was not provided, an Operation (either the
    original one or the Replacement) is delivered to the base level
    Object.</li>

    <li>The Result of the Operation will be presented to the
    MetaObject if it asked so.  The MetaObject may provide a a
    replacement Result, that will be returned as if it were the actual
    Result of the Operation.</li>

    </ul>

    <p>MetaObjects should be made simple.  Instead of overloading a
    single MetaObject with dozens of meta-level requirements,
    several MetaObjects should be created, each one implementing one
    of the requirements.  This enhances meta-level code simplicity and
    reusability.

    <p>The relationship between MetaObjects and base-level Objects is
    dynamic: it is possible to change at any time the set of
    MetaObjects associated with an Object.  A single MetaObject may be
    associated with several Objects, but simpler MetaObjects may
    prefer to be associated with one Object at a time.

    <p>MetaObjects may create Operations targeted to Objects they are
    associated with at any time, as long as they hold an
    OperationFactory for that Object.  Although OperationFactories
    usually provide full access to the Object, Composers are allowed
    to create OperationFactoryFilters that impose restrictions on the
    kind of Operations MetaObjects they delegate to may create.
    Furthermore, they may refuse to accept Results returned by a
    MetaObject they delegate to.

    @see Composer
    @see OperationFactory
    @see OperationFactoryFilter

    @author Alexandre Oliva
    @version $Revision: 1.24 $  */
public abstract class MetaObject {
  /** Creates a MetaObject.  */
  public MetaObject() {}
  
  /** This method is invoked before an Operation is delivered to the
      base-level Object.  The MetaObject can emulate or simulate the
      Operation, and then return its Result, or provide a replacement
      Operation, to be executed instead of the requested one, or none
      of the above.  If it does not return a Result, it may ask to be
      presented the Result of the performed Operation, and even to be
      able to change them.

      @param operation contains information about the base-level
      Object the Operation is targeted to, the kind of Operation and
      its arguments.

      @param object the target Object of the operation, which should
      be the value returned by operation.getObject()

      @return a Result object, that may contain a value to be regarded
      as the actual Result of the Operation, a replacement Operation,
      to be performed instead of the one provided, or just a request
      to be informed about its Result or not.  If a replacement
      Operation is provided, the request still holds.  A null return
      value should be interpreted as a request for no further
      information.  The default behavior is to return null.

      @see Operation  */
  public Result handle(final Operation operation,
		       final Object object) {
    return null;
  }

  /** A MetaObject is told the Result of an Operation by the
      invocation of this method.  This method should only be invoked
      if the MetaObject was formerly requested to handle the Operation
      the Result refers to, even if that Operation was replaced by
      another.

      <p>MetaObjects can match replaced Operations with their Results
      by following the Operation replacement chain using the
      getReplaced() method, or by storing the original Operation of a
      chain, determined with getOriginal().

      <p>If the Operation Handle method had returned a request to
      modifyResult, it may now return any other Result.  If it had
      returned inspectResult, the caller may disregard the return
      value of this method.  Even if a MetaObject asked not to be
      informed of a Result, it should be prepared to accept it and
      ignore it.

      @param result the Result of the Operation.

      @param object the target Object of the operation, which should
      be the value returned by result.getOperation().getObject().

      @return a Result with a value, either returned or thrown, to be
      used instead of the specified in the invocation.  Any other kind
      of Result, or null, means the previously known Result should be
      used.  If the MetaObject had not requested to change the Result,
      it must return null or the specified result.  The default
      behavior is to return null.

      @see Operation#getReplaced
      @see Operation#getOriginal  */
  public Result handle(final Result result,
		       final Object object) {
    return null;
  }

  /** This method is invoked whenever a Message is Broadcast to the
      meta-configuration of an Object.  It may be used for
      communication between MetaObjects of different Objects.  It may
      be specialized and the Message may be used for whatever purposes
      it fits.  Composers are expected to forward Messages to other
      MetaObjects they delegate to.

      @param message the Message to be sent to the components of the
      Object's meta-configuration.

      @param object the Object whose meta-configuration components
      should receive the Message.  */
  public void handle(Message message, Object object) {}

  /** This method requests the MetaObject to create a new MetaObject
      for a base-level Object, and to configure it as appropriate.

      <p>When an object is created, every MetaObject associated with
      the creator Object may be asked to indicate a MetaObject to take
      its place in the new Object's meta-configuration.  This is
      usually a new MetaObject created for this purpose or null, but
      it may be any existing one, even the asked MetaObject itself.

      <p>This method should be overridden by any subclass that intends
      to control the creation of MetaObjects for new Objects.
      Subclasses are encouraged to completely override this method, as
      the default implementation simply returns null.

      @param newObject the new Object a MetaObject should be provided
      for.

      @param object the Object the existing MetaObject is associated
      with.

      @return the MetaObject to be associated with the base level
      Object.  The default behavior is to return null.  */
  public MetaObject configure(final Object newObject,
			      final Object object) {
    return null;
  }

  /** This method is invoked to check whether a existing MetaObject
      accepts to be replaced with another.

      <p>It will be invoked whenever one asks to Reconfigure an Object
      that is associated with a MetaObject already.  The already
      associated MetaObject is told the intended new
      meta-configuration, and it should return the head MetaObject of
      whatever meta-configuration it decides is appropriate.

      <p>The default behavior is to accept the new MetaObject.
      Although this provides for reconfigurability, any MetaObject
      that cares about being kept associated with the Object should
      override this method.  Reasonable implementations include:

      <ul>

      <li>create a Composer that delegates to both the old and the new
      MetaObject, and return it.</li>

      <li>return <tt>this</tt>, which means the new MetaObject is
      rejected.</li>

      <li>analyse the new meta-configuration and plug itself in
      it.</li>

      <li>plug the new meta-configuration in the existing one.</li>

      <li>whatever seems reasonable for that pair of MetaObjects.</li>

      </ul>

      @param newMetaObject the head MetaObject of the intended
      meta-configuration.

      @param object the Object it wants to be associated with.  */
  public MetaObject reconfigure(final Object object,
				final MetaObject oldMetaObject,
				final MetaObject newMetaObject) {
    if (this == oldMetaObject)
      return newMetaObject;
    else
      return oldMetaObject;
  }

  /** Just before a MetaObject is associated with an Object, this
      method should be invoked.  This allows the MetaObject to
      initialize any object-related information.  Whenever a Configure
      or a Reconfigure Operation takes place, this method is invoked
      for the MetaObject directly associated with the base-level
      Object.  It is responsible for delegating this initialization to
      any other MetaObjects it may delegate Operations to.

      <p>A MetaObject may be initialized more than once.  This will
      happen whenever an Object's meta-configuration is changed, but
      the MetaObject is both in the old and the new
      meta-configuration.  In this case, it should have Initialize
      invoked before Release, so that it may keep track of the
      duplicate initialization and keep information about the
      base-level Object, instead of discarding it in the first
      Release.  Composers should behave similarly, initializing and
      releasing MetaObjects in the appropriate sequence.

      <p>The default behavior is to completely ignore this invocation.

      @param factory the OperationFactory to be used by the MetaObject
      to create Operations targeted to the object.

      @param object the Object the MetaObject is being associated
      with.  */
  public void initialize(final OperationFactory factory,
			 final Object object) {}

  /** A MetaObject should be informed it is being detached from an
      Object by an invocation of this method.  Invocations of Release
      must match invocations of Initialize.  If Release was invoked as
      many times as Initialize, the MetaObject may discard information
      about the base-level Object.  From then on, it must not be
      requested to handle any Operation targeted to that Object.

      <p>The default behavior is to ignore this invocation.

      @param obj the Object the MetaObject is being detached from.  */
  public void release(Object obj) {}
}
