/*
   Copyright 2007 Attila Szegedi

   Licensed 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.
*/
package org.dynalang.mop;

import java.lang.reflect.UndeclaredThrowableException;
import java.util.Iterator;
import java.util.Map;

/**
 * The base metaobject protocol interface that provides essential functionality
 * for implementing a metaobject protocol. Some notes on the design:
 * <ul>
 *   <li>No methods throw checked exceptions. If a method's implementation 
 *   relies on calling Java methods that throw checked exceptions (most likely
 *   the various <tt>call</tt> methods), then they must wrap them into a 
 *   {@link UndeclaredThrowableException} first.</li>
 *   <li>Various expected failures are not indicated by exceptions, but rather
 *   indicated with special return values, either by returning null from 
 *   methods that otherwise return a Boolean, or more frequently, by returning 
 *   a special value from the {@link Result} enum where methods return 
 *   {@link Object}. The rationale for this is that the protocols are designed 
 *   with chaining in mind, namely more than one protocol can be invoked for
 *   an operation on an object, and certain protocols can declare that they
 *   can't handle that particular object (in our self-chosen terminology,
 *   they don't have authority over it) so the next protocol gets tried. 
 *   Using exceptions for such expected flow control seemed like a bad 
 *   practice. Also, a language implementation using multiple protocols can 
 *   choose to signal an error on its own if all protocols are exhausted 
 *   without success for a particular operation, and it is at liberty to use
 *   whatever is its natural error signaling method, which is not necessarily
 *   equivalent or best implemented using Java structured exception handling.
 *   </li>
 * </ul>
 *   
 * @author Attila Szegedi
 * @version $Id: $
 */
public interface BaseMetaobjectProtocol extends CallProtocol {
    
    /**
     * Defines special operation results that are used as return values from
     * many metaobject protocol methods to indicate success or failure.
     * @author Attila Szegedi
     * @version $Id: $
     */
    public static enum Result {
	/**
	 * The requested property does not exist.
	 */
        doesNotExist,
	/**
	 * The metaobject protocol can't authoritatively perform the requested
	 * operation on the object (the object is foreign to it).
	 */
        noAuthority,
	/**
	 * The target object that was attempted to be called does not support
	 * calling (is not a callable) in the context of the attempted call
	 * operation (either with positional or named arguments).
	 */
        notCallable,
	/**
	 * The property attempted to be deleted can not be deleted.
	 */
        notDeleteable,
	/**
	 * The property attempted to be read exists, but is not readable.
	 */
        notReadable,
	/**
	 * A suitable type representation for a value could not be obtained.
	 */
        noRepresentation,
	/**
	 * The property attempted to be written exists, but is not writable.
	 */
        notWritable,
	/**
	 * The operation succeeded.
	 */
        ok,
    };
    
    /* -- Property access --*/

    /**
     * Deletes an association of a value with a property in the target object.
     * @param target the target object
     * @param propertyId the ID of the property. Usually a String or an 
     * Integer, but other property ID types can also be supported.
     * @return the status of the operation. If the protocol decides that the 
     * property did not previously exist, it will return 
     * {@link BaseMetaobjectProtocol.Result#doesNotExist}. If the protocol authoritatively decides 
     * that the property can not be deleted, it will return 
     * {@link BaseMetaobjectProtocol.Result#notDeleteable}. If the protocol doesn't have the 
     * authority to delete the property, it will leave the object unchanged, 
     * and return {@link BaseMetaobjectProtocol.Result#noAuthority}. If the operation succeeds, 
     * {@link BaseMetaobjectProtocol.Result#ok} is returned.
     */
    public Result delete(Object target, Object propertyId);

    /**
     * Tells whether the target object has a particular property.
     * @param target the target object
     * @param propertyId the ID of the property. Usually a String or an 
     * Integer, but other property ID types can also be supported.
     * @return {@link Boolean#TRUE} or {@link Boolean#FALSE} if the protocol 
     * can authoritatively decide whether the target has or has not the 
     * property, or null if it does not have the authority to decide.
     */
    public Boolean has(Object target, Object propertyId);
    
    /**
     * Returns an iterator over the property ID-value pairs in the target 
     * object this MOP knows about (and chooses to expose - not all properties 
     * must be exposed; hidden properties are allowed).
     * @param target the target object
     * @return an iterator over all the property ID-value pairs.
     */
    public Iterator<Map.Entry> properties(Object target);
    
    /* -- Iteration --*/

    /**
     * Associates a value with a property in the target object.
     * @param target the target object
     * @param propertyId the ID of the property. Usually a String or an 
     * Integer, but other property ID types can also be supported.
     * @param value the new value for the property
     * @param callProtocol a marshaller that is used to convert the value in
     * case the property can only accept values of certain types, and the 
     * metaobject protocol allows automatic coercing to those types.
     * @return a value indicating the outcome of the operation. If the protocol 
     * decides that the property does not exist, and can not be created, it 
     * will return {@link BaseMetaobjectProtocol.Result#doesNotExist}. If the protocol 
     * authoritatively decides that the property is read-only, it will return 
     * {@link BaseMetaobjectProtocol.Result#notWritable}. If the property has a limitation on types
     * it can take as values, and the supplied marshaller is not able to 
     * provide a suitable representation, then the value of the property is not
     * changed, and {@link BaseMetaobjectProtocol.Result#noRepresentation} is returned. Finally, if
     * the protocol doesn't have the authority to put the new property value, 
     * it will leave the property value unchanged, and return 
     * {@link BaseMetaobjectProtocol.Result#noAuthority}. If the operation succeeds, 
     * {@link BaseMetaobjectProtocol.Result#ok} is returned.
     */
    public Result put(Object target, Object propertyId, Object value, 
            CallProtocol callProtocol);
}