/*
 * Copyright 1999-2006 University of Chicago
 *
 * 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.globus.rsl;

import java.util.*;

/**
 * This class represents an abstract RSL parse tree. It is composed of variable definitions
 * (bindings), relations, and sub-specifications (sub nodes).
 */
public abstract class AbstractRslNode {

    public static final int AND   = 1;
    public static final int OR    = 2;
    public static final int MULTI = 3;

    protected int _operator;
    protected List _specifications;

    public AbstractRslNode() {
	setOperator(AND);
    }

    public AbstractRslNode(int operator) {
	setOperator(operator);
    }

    public abstract boolean add(Bindings bindings);

    public abstract boolean add(NameOpValue relations);

    /**
     * Adds a rsl parse tree to this node.
     *
     * @param node the rsl parse tree to add.
     */
    public boolean add(AbstractRslNode node) {
        if (_specifications == null) _specifications = new LinkedList();
        return _specifications.add(node);
    }

    /**
     * Returns the relation associated with the given attribute.
     *
     * @param attribute the attribute of the relation.
     * @return the relation for the attribute. Null, if not found.
     */
    public abstract NameOpValue getParam(String attribute);

    /**
     * Returns the variable definitions associated wit the given
     * attribute.
     *
     * @param attribute the attribute of the variable deinitions.
     * @return the variable deinitions for the attribute.
     *         Null, if not found.
     */
    public abstract Bindings getBindings(String attribute);

    /**
     * Removes a specific sub-specification tree from the
     * sub-specification list.
     *
     * @param node node to remove.
     * @return true if the tree was removed successfuly. False,
     *         otherwise.
     */
    public boolean removeSpecification(AbstractRslNode node) {
        if (_specifications == null || node == null) return false;
        return _specifications.remove(node);
    }

    /**
     * Removes a bindings list for the specified attribute.
     *
     * @param attribute the attribute name for the
     *        bindings.
     * @return the bindings that were removed.
     */
    public abstract Bindings removeBindings(String attribute);

    /**
     * Removes a relation for the specified attribute.
     *
     * @param attribute the attribute name for the
     *        relation to remove.
     * @return the relation that was removed.
     */
    public abstract NameOpValue removeParam(String attribute);

    /**
     * Merges the specified node with the current node.
     * All sub-specifications from the given node will be
     * copied to the current node. All relations and variable
     * definitions will be added together in the current node.
     *
     * @param inNode the source parse tree.
     */
    public void merge(AbstractRslNode inNode) {
	inNode.mergeTo(this);
    }

    public void mergeTo(AbstractRslNode dstNode) {
        Iterator iter = null;
        if (_specifications != null) {
            iter = _specifications.iterator();
            AbstractRslNode node;
            while(iter.hasNext()) {
                node = (AbstractRslNode)iter.next();
                dstNode.add(node);
            }
        }
    }

    /**
     * Returns the list of sub-specifications.
     *
     * @return the list of other sub-specifications.
     */
    public List getSpecifications() {
	return _specifications;
    }

    /**
     * Returns the node operator.
     *
     * @return the operator.
     */
    public int getOperator() {
	return _operator;
    }

    /**
     * Sets the operator.
     *
     * @param oper the operator.
     */
    public void setOperator(int oper) {
	_operator = oper;
    }

    /**
     * Returns the operator as a string.
     *
     * @return operator in a string representation.
     */
    public String getOperatorAsString() {
	return getOperatorAsString(_operator);
    }

    /**
     * Returns a string represention of a given operator.
     *
     * @param op the operator.
     * @return the string representation of the operator.
     */
    public static String getOperatorAsString(int op) {
	switch(op) {
	case AND:
	    return "&";
	case MULTI:
	    return "+";
	case OR:
	    return "|";
	default:
	    return "??";
	}
    }

    /**
     * Evalutes the rsl tree.
     * All the variable definitions are first evaluated because
     * they might update the symbol table. Then all the relations
     * followed by the sub-specifications are evaluated.
     *
     * @return the evaluated rsl tree.
     * @exception RslEvaluationException If an error occured during
     *            rsl evaluation.
     */
    public AbstractRslNode evaluate()
	throws RslEvaluationException {
	return evaluate(null);
    }

    /**
     * Evalutes the rsl tree against the specified symbol table.
     * All the variable definitions are first evaluated because
     * they might update the symbol table. Then all the relations
     * followed by the sub-specifications are evaluated.
     *
     * @param symbolTable the symbol table to evalute variables against.
     * @return the evaluated rsl tree.
     * @exception RslEvaluationException If an error occured during
     *            rsl evaluation.
     */
    public abstract AbstractRslNode evaluate(Map symbolTable)
	throws RslEvaluationException;

    /**
     * Returns a RSL representation of this relation. <BR>
     * <I>Note: Enable explicitConcat to generate more 'valid' RSL</I>
     *
     * @param explicitConcat if true explicit concatination will
     *        be used in RSL strings.
     * @return RSL representation of this relation.
     */
    public String toRSL(boolean explicitConcat) {
	StringBuffer buf = new StringBuffer();
	toRSL(buf, explicitConcat);
	return buf.toString();
    }

    /**
     * Produces a RSL representation of node.
     *
     * @param buf buffer to add the RSL representation to.
     * @param explicitConcat if true explicit concatination will
     *        be used in RSL strings.
     */
    public abstract void toRSL(StringBuffer buf, boolean explicitConcat);

    public String toString() {
	return toRSL(true);
    }

    /**
     * Canonicalizes a string by removing any underscores and
     * moving all characters to lowercase.
     *
     * @param str string to canonicalize
     * @return canonicalized string
     */
    public static String canonicalize(String str) {
	if (str == null) return null;
	int length = str.length();
	char ch;
	StringBuffer buf = new StringBuffer(length);
	for (int i=0;i<length;i++) {
	    ch = str.charAt(i);
	    if (ch == '_') continue;
	    buf.append( Character.toLowerCase(ch) );
	}
	return buf.toString();
    }

}
