// 
// The Waveset SPML General License 
// 
// Version 0.1, April 2003
// Copyright (C) 2003 Waveset Technologies, Inc..
// 6034 West Courtyard Drive, Suite 210, Austin, Texas 78730
// All rights reserved.
// 
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// 
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions, and the disclaimers in Sections 6
//    and 7 below.
// 
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the disclaimers in Sections 6
//    and 7 below in the documentation and/or other materials provided
//    with the distribution.
// 
// 3. The end-user documentation included with the redistribution, if
//    any, must include the following acknowledgment:
// 
//    "This product includes software developed by 
//     Waveset Technologies, Inc. (www.waveset.com)."
// 
//    Alternately, this acknowledgment may appear in the software itself, if
//    and wherever such third-party acknowledgments normally appear.
//  
// 4. The names "Waveset" and "Waveset Technologies, Inc." must not be
//    used to endorse or promote products derived from this software
//    without the prior written permission of Waveset. For written
//    permission, please contact www.waveset.com.
//  
// 5. Products derived from this software may not be called "Waveset",
//    nor may "Waveset" appear in their name, without the prior written
//    permission of Waveset.
//  
// 6. NO WARRANTY 
// 
//    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED
//    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//    DISCLAIMED.
// 
// 7.  LIMITATION OF LIABILITY
// 
//    IN NO EVENT SHALL THE WAVESET OR ITS LICENSORS BE LIABLE FOR ANY
//    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
//    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
//    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
//    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// End of Terms and Conditions
// ====================================================================
//
// Component Name: org.openspml.message.AddRequest
//
// Author(s): Jeff Larson
//
// Description:
//
// Memory representation for one term in a <filter> expression.
// A number of schema concepts are collapsed into a single
// class for simplicity.
//
//

package org.openspml.message;

import java.util.*;
import org.openspml.util.*;

/**
 * A class used to represent one filter expression term, found
 * within a <code>filter</code> element of a <code>searchRequest</code>
 * message.
 * <p>
 * The memory model could be decomposed into a more specific
 * set of classes, but for simplicity all terms are represented
 * by a single class.  The term "operation" must be consulted
 * to determine which of the other term properties are relevant.
 */
public class FilterTerm {

    //////////////////////////////////////////////////////////////////////
    //
    // Constants
    //
    //////////////////////////////////////////////////////////////////////

    //
    // Filter "opcodes", defined by FilterGroup in the schema
    // These are defined by DSML, note that they do not use
    // an enumeration prefix    
    //

    /**
     * Constant used with <code>setOperation</code>
     * to specify the "and" operator.  
     * When this operation is set,
     * a list of operands may be specified with <code>setOperands</code>
     */
    public static final String OP_AND = "and";

    /**
     * Constant used with <code>setOperation</code> to specify
     * the "or" operator.  
     * When this operation is set,
     * a list of operands may be specified with <code>setOperands</code>
     */
    public static final String OP_OR = "or";

    /**
     * Constant used with <code>setOperator</code> to specify the
     * "not" operator.  
     * When this operation is set, there is expected to be a single
     * item on the operands list.
     */
    public static final String OP_NOT = "not";

    /**
     * Constant used with <code>setOperator</code> to specify
     * an equality comparison.
     * When this operation is set, the <code>name</code> and
     * <code>values</code> fields must also be set.
     */
    public static final String OP_EQUAL = "equalityMatch";

    /**
     * Constant used with <code>setOperator</code> to specify
     * an substring comparison.
     * When this operation is set, at least one of the
     * <code>initialSubstring</code>, <code>finalSubstring</code>,
     * or <code>substrings</code> fields should be set.
     */
    public static final String OP_SUBSTRINGS = "substrings";

    /**
     * Constant used with <code>setOperator</code> to specify
     * a greater than or equal comparison.      
     * When this operation is set, the <code>name</code> and
     * <code>values</code> fields must also be set.
     */
    public static final String OP_GTE = "greaterOrEqual";

    /**
     * Constant used with <code>setOperator</code> to specify
     * a less than or equal comparison.      
     * When this operation is set, the <code>name</code> and
     * <code>values</code> fields must also be set.
     */
    public static final String OP_LTE = "lessOrEqual";

    /**
     * Constant used with <code>setOperator</code> to specify
     * a "present" comparison.
     */
    public static final String OP_PRESENT = "present";

    /**
     * Constant used with <code>setOperator</code> to specify
     * an "approxomate match" comparison.
     */
    public static final String OP_APPROX = "approxMatch";

    /**
     * Constant used with <code>setOperator</code> to specify
     * an "extensible match" comparison.
     */
    public static final String OP_EXTENSIBLE_MATCH = "extensibleMatch";

    //////////////////////////////////////////////////////////////////////
    //
    // Fields
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * The "opcode" for this term.  Defined by FilterGroup.
     */
    String _op;

    /**
     * List of operands for AND, OR, NOT.
     * For NOT there should be only one term in the list.
     */
    List _operands;

    /**
     * Attribute name for everything except AND, OR, NOT.
     */
    String _name;

    /**
     * List of attribute values for comparison.
     */
    List _values;

    /**
     * Initial substring for OP_SUBSTIRNGS.
     */
    String _initialSubstring;
    
    /**
     * Final substring for OP_SUBSTRINGS.
     */
    String _finalSubstring;

    /**
     * Unrooted substrings for OP_SUBSTRINGS.
     */
    List _substrings;

    /**
     * Used with <extensibleMatch>, not sure what it means.
     */
    boolean _dnAttributes;

    /**
     * Used with <extensibleMatch>, the name of the system specific
     * matching rule to use.
     */
    String _matchingRule;

    //////////////////////////////////////////////////////////////////////
    //
    // Constructors
    //
    //////////////////////////////////////////////////////////////////////

    public FilterTerm() {
    }

    FilterTerm(XmlElement e) {
        parseXml(e);
    }

    public FilterTerm(String op) {
        _op = op;
    }

    public void setOperation(String op) {
        _op = op;
    }

    public void setOperands(List terms) {
        _operands = terms;
    }

    public void addOperand(FilterTerm term) {
        if (term != null) {
            if (_operands == null)
                _operands = new ArrayList();
            _operands.add(term);
        }
    }

    public void setName(String s) {
        _name = s;
    }

    public void setValue(Object o) {     
	_values = new ArrayList();
	_values.add(o);
    }

    public void setValues(List l) {     
        _values = l;
    }

    public void addValue(Object o) {
        if (o != null) {
            if (_values == null)
                _values = new ArrayList();
            _values.add(o);
        }
    }

    public void setInitialSubstring(String s) {
        _initialSubstring = s;
    }

    public void setFinalSubstring(String s) {
        _finalSubstring = s;
    }

    public void setSubstrings(List l) {
        _substrings = l;
    }

    public void addSubstring(String s) {
        if (s != null) {
            if (_substrings == null)
                _substrings = new ArrayList();
            _substrings.add(s);
        }
    }

    public void setDnAttributes(boolean b) {
        _dnAttributes = b;
    }

    public void setMatchingRule(String s) {
        _matchingRule = s;
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Accessors
    //
    //////////////////////////////////////////////////////////////////////

    public String getOperation() {
        return _op;
    }

    public boolean isOperation(String op) {
        return _op.equals(op);
    }

    public List getOperands() {
        return _operands;
    }

    public String getName() {
        return _name;
    }

    public List getValues() {
        return _values;
    }

    public Object getValue() {
        Object value = null;
        if (_values != null && _values.size() > 0)
            value = _values.get(0);
        return value;
    }

    public String getInitialSubstring() {
        return _initialSubstring;
    }

    public String getFinalSubstring() {
        return _finalSubstring;
    }

    public List getSubstrings() {
        return _substrings;
    }

    public boolean isDnAttributes() {
        return _dnAttributes;
    }

    public String getMatchRule() {
        return _matchingRule;
    }

    //////////////////////////////////////////////////////////////////////
    //
    // XML
    //
    //////////////////////////////////////////////////////////////////////

    void toXml(SpmlBuffer b) {

        boolean isbool = 
            _op.equals(OP_AND) || _op.equals(OP_OR) || _op.equals(OP_NOT);

	// We're now in the dsml:Filter type, so elements must
	// be qualified.
        // UPDATE: Now that the containing <dsml:filter> is namespace
        // qualified, can we avoid qualification here??

        b.addOpenStartTag("dsml", _op);

	// do we need to qualify attributes too?
        if (!isbool)
            b.addAttribute("name", _name);

        if (_op.equals(OP_EXTENSIBLE_MATCH)) {
            b.addAttribute("matchingRule", _matchingRule);
            b.addAttribute("dnAttributes", _dnAttributes);
        }

        b.closeStartTag();
        b.incIndent();

        if (isbool) {
            if (_operands != null) {
                Iterator it = _operands.iterator();
                while (it.hasNext()) {
                    FilterTerm ft = (FilterTerm)it.next();
                    ft.toXml(b);
                }
            }
        }
        else if (_op.equals(OP_SUBSTRINGS)) {

            if (_initialSubstring != null)
                b.addElement("dsml", "initial", _initialSubstring);

            if (_substrings != null) {
                Iterator it = _substrings.iterator();
                while (it.hasNext()) {
                    b.addStartTag("dsml", "any", false);
                    b.addContent((String)it.next());
                    b.addEndTag("dsml", "any", false);
                }
            }

            if (_finalSubstring != null)
                b.addElement("dsml", "final", _finalSubstring);
        }
        else if (_values != null) {
            Iterator it = _values.iterator();
            while (it.hasNext()) {
                b.addStartTag("dsml", "value", false);
                b.addContent((String)it.next());
                b.addEndTag("dsml", "value", false);
            }
        }

        b.decIndent();
        b.addEndTag("dsml", _op);
    }

    void parseXml(XmlElement e) {

        _op = e.getLocalName();
        _name = e.getAttribute("name");
        _matchingRule = e.getAttribute("matchingRule");
        _dnAttributes = e.getBooleanAttribute("dnAttributes");

        for (XmlElement child = e.getChildElement() ;  
             child != null ; child = child.next()) {
            
            String tag = child.getLocalName();

            if (tag.equals("initial"))
                _initialSubstring = child.getContent();

            else if (tag.equals("final"))
                _finalSubstring = child.getContent();
            
            else if (tag.equals("any"))
                addSubstring(child.getContent());

            else if (tag.equals("value"))
                addValue(child.getContent());
            
            else {
                // it must be an operand
                addOperand(new FilterTerm(child));
            }
        }
    }


}
