/*
   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.util.Iterator;
import java.util.Map;

import org.dynalang.mop.impl.MetaobjectProtocolAdaptor;
import org.dynalang.mop.impl.MetaobjectProtocolBase;

/**
 * An extension of {@link BaseMetaobjectProtocol} that can provide various
 * convenience functionality that can implemented in terms of base 
 * functionality, but the specialized implementations can be optimized. To 
 * easily adapt an existing {@link BaseMetaobjectProtocol} into a full-fledged 
 * {@link MetaobjectProtocol}, you can use {@link MetaobjectProtocolAdaptor}.
 * Alternatively, you can easily get a full-fledged {@link MetaobjectProtocol} 
 * if you subclass {@link MetaobjectProtocolBase} and only implement 
 * {@link BaseMetaobjectProtocol} methods on it.
 * @author Attila Szegedi
 * @version $Id: $
 */
public interface MetaobjectProtocol extends BaseMetaobjectProtocol {
    /**
     * Calls a method on the target object with supplied named arguments. 
     * Must behave identically to:
     * <pre>
     * Object callable = get(target, callableId);
     * if(callable instanceof Result) {
     *     return callable;
     * }
     * return call(callable, args);
     * </pre>
     * @param target the target object
     * @param callableId the ID of the method to call
     * @param callProtocol a marshaller that should be used by this
     * metaobject protocol to convert the arguments to conform to expected
     * argument types for the call.
     * @param args the arguments of the call
     * @return the result of the invocation, or a special result. Can return
     * any return value that {@link BaseMetaobjectProtocol#get(Object, Object)}
     * would return while looking up the callable. Can also return any return
     * value that {@link BaseMetaobjectProtocol#call(Object, CallProtocol, Map)}
     * would return while calling.
     */
    public Object call(Object target, Object callableId, CallProtocol callProtocol, 
	    Map args);

    /**
     * Calls a method on the target object with supplied positional arguments. 
     * Must behave identically to:
     * <pre>
     * Object callable = get(target, callableId);
     * if(callable instanceof Result) {
     *     return callable;
     * }
     * return call(callable, args);
     * </pre>
     * @param target the target object
     * @param callableId the ID of the method to call
     * @param callProtocol a marshaller that should be used by this
     * metaobject protocol to convert the arguments to conform to expected
     * argument types for the call.
     * @param args the arguments of the call
     * @return the result of the invocation, or a special result. Can return
     * any return value that {@link BaseMetaobjectProtocol#get(Object, Object)}
     * would return while looking up the callable. Can also return any return
     * value that {@link BaseMetaobjectProtocol#call(Object, CallProtocol, Object[])}
     * would return while calling.
     */
    public Object call(Object target, Object callableId, 
	    CallProtocol callProtocol, Object... args);

    /**
     * Behaves as {@link BaseMetaobjectProtocol#delete(Object, Object)} with an 
     * integer property ID.
     * @param target the target object
     * @param propertyId the ID of the property.
     * @return see {@link BaseMetaobjectProtocol#delete(Object, Object)}.
     */
    public Result delete(Object target, long propertyId);

    /**
     * Behaves as {@link BaseMetaobjectProtocol#get(Object, Object)} with an 
     * integer property ID.
     * @param target the target object
     * @param propertyId the ID of the property.
     * @return see {@link BaseMetaobjectProtocol#get(Object, Object)}.
     */
    public Object get(Object target, long propertyId);

    /* -- Iteration --*/

    /**
     * Behaves as {@link BaseMetaobjectProtocol#has(Object, Object)} with an 
     * integer property ID.
     * @param target the target object
     * @param propertyId the ID of the property.
     * @return see {@link BaseMetaobjectProtocol#has(Object, Object)}.
     */
    public Boolean has(Object target, long propertyId);
    
    /* -- Direct call --*/

    /**
     * Returns an iterator over the property IDs 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 the property IDs.
     */
    public Iterator<? extends Object> propertyIds(Object target);


    /**
     * Behaves as {@link BaseMetaobjectProtocol#put(Object, Object, Object,CallProtocol)} 
     * with an integer property ID.
     * 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 the previous value of the property, or a special value. A null 
     * value means that the protocol authoritatively decided that the previous
     * value of the property is null. 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 can not set the new value,
     * it will return {@link BaseMetaobjectProtocol.Result#notWritable}. If the protocol 
     * doesn't have the authority to put the new property value, it will leave 
     * the object unchanged, and return {@link BaseMetaobjectProtocol.Result#noAuthority}. If the
     * operation succeeds, {@link BaseMetaobjectProtocol.Result#ok} is returned.
     */
    public Result put(Object target, long propertyId, Object value, CallProtocol callProtocol);
}