/*
 * Smart GWT (GWT for SmartClient)
 * Copyright 2008 and beyond, Isomorphic Software, Inc.
 *
 * Smart GWT is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.  Smart GWT is also
 * available under typical commercial license terms - see
 * http://smartclient.com/license
 *
 * This software 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
 * Lesser General Public License for more details.
 */
/* sgwtgen */
 
package com.smartgwt.client.data;


import com.smartgwt.client.event.*;
import com.smartgwt.client.core.*;
import com.smartgwt.client.types.*;
import com.smartgwt.client.data.*;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.events.*;
import com.smartgwt.client.browser.window.*;
import com.smartgwt.client.rpc.*;
import com.smartgwt.client.ai.*;
import com.smartgwt.client.callbacks.*;
import com.smartgwt.client.tools.*;
import com.smartgwt.client.bean.*;
import com.smartgwt.client.widgets.*;
import com.smartgwt.client.widgets.ai.*;
import com.smartgwt.client.widgets.events.*;
import com.smartgwt.client.widgets.form.*;
import com.smartgwt.client.widgets.form.validator.*;
import com.smartgwt.client.widgets.form.fields.*;
import com.smartgwt.client.widgets.tile.*;
import com.smartgwt.client.widgets.tile.events.*;
import com.smartgwt.client.widgets.grid.*;
import com.smartgwt.client.widgets.grid.events.*;
import com.smartgwt.client.widgets.chart.*;
import com.smartgwt.client.widgets.layout.*;
import com.smartgwt.client.widgets.layout.events.*;
import com.smartgwt.client.widgets.menu.*;
import com.smartgwt.client.widgets.tour.*;
import com.smartgwt.client.widgets.notify.*;
import com.smartgwt.client.widgets.rte.*;
import com.smartgwt.client.widgets.rte.events.*;
import com.smartgwt.client.widgets.ace.*;
import com.smartgwt.client.widgets.ace.events.*;
import com.smartgwt.client.widgets.tab.*;
import com.smartgwt.client.widgets.toolbar.*;
import com.smartgwt.client.widgets.tree.*;
import com.smartgwt.client.widgets.tree.events.*;
import com.smartgwt.client.widgets.tableview.*;
import com.smartgwt.client.widgets.viewer.*;
import com.smartgwt.client.widgets.calendar.*;
import com.smartgwt.client.widgets.calendar.events.*;
import com.smartgwt.client.widgets.cube.*;
import com.smartgwt.client.widgets.notify.*;
import com.smartgwt.client.widgets.drawing.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.event.shared.*;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.core.client.JavaScriptObject;

import com.smartgwt.client.util.*;
import com.smartgwt.client.util.events.*;
import com.smartgwt.client.util.workflow.*;
import com.smartgwt.client.util.workflow.Process; // required to override java.lang.Process
import com.smartgwt.client.util.tour.*;


/**
 * An object representing a criterion to apply to a record.   <P> A criterion is part of the definition of an {@link
 * com.smartgwt.client.data.AdvancedCriteria} object, which is used to filter records according to search criteria.  <P> A
 * criterion consists of an {@link com.smartgwt.client.data.Criterion#getOperator operator} and typically a {@link
 * com.smartgwt.client.data.DataSourceField#getName fieldName} from a {@link com.smartgwt.client.data.Record} and a {@link
 * com.smartgwt.client.data.Criterion#getValue value} to compare to.  However some operators either don't require a value
 * (eg, isNull) or act on other criteria rather than directly on a {@link com.smartgwt.client.data.Record}'s fields (eg,
 * the "and" and "or" logical operators).   Also, it is possible to  specify a {@link
 * com.smartgwt.client.data.Criterion#getFieldQuery fieldQuery} instead of a <code>fieldName</code> and/or a  {@link
 * com.smartgwt.client.data.Criterion#getValueQuery valueQuery} instead of a <code>value</code> <P> A shortcut form is also
 * allowed where only <code>fieldName</code> and <code>value</code> values are provided. In this case the
 * <code>operator</code> is assumed to be "equals".
 * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
 */
@BeanFactory.FrameworkClass
public class Criterion extends Criteria {

    public static Criterion getOrCreateRef(JavaScriptObject jsObj) {
        if(jsObj == null) return null;
        return new Criterion(jsObj);
    }
        


    public Criterion(){
        
    }

    public Criterion(JavaScriptObject jsObj){
        
        setJavaScriptObject(jsObj);
    }



    // ********************* Properties / Attributes ***********************

    /**
     * For a criterion with an operator that acts on other criteria (eg "and", "or"), a list of sub-criteria that are grouped
     * together by the operator.
     *
     * @param criteria New criteria value. Default value is null
     * @return {@link com.smartgwt.client.data.Criterion Criterion} instance, for chaining setter calls
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public Criterion setCriteria(Criterion... criteria) {
        return (Criterion)setAttribute("criteria", criteria);
    }

    /**
     * For a criterion with an operator that acts on other criteria (eg "and", "or"), a list of sub-criteria that are grouped
     * together by the operator.
     *
     * @return Current criteria value. Default value is null
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public Criterion[] getCriteria()  {
        return com.smartgwt.client.util.ConvertTo.arrayOfCriterion(getAttributeAsJavaScriptObject("criteria"));
    }
    
    

    /**
     * Name of the field in each {@link com.smartgwt.client.data.Record} that this criterion applies to.  Not applicable for a
     *  criterion with {@link com.smartgwt.client.data.Criterion#getCriteria sub-criteria}. Can be specified as a dataPath to 
     *  allow matching nested objects. Use '/' as delimiters for dataPath. See 
     *  {@link com.smartgwt.client.docs.DataPath dataPath} for more information.
     * 
     *  <p>
     *  <h4>{@link com.smartgwt.client.data.Criterion#getFieldQuery fieldQuery} shortcuts</h4>
     *  <code>fieldName</code> can also be used to express a compact form of related-field filtering.
     *  If you set this property to the qualified name of a field on a related DataSource, it will
     *  be transformed into a basic {@link com.smartgwt.client.data.AdvancedCriterionSubquery}.  For example, say you 
     *  have an <code>Order</code> dataSource, which has a 
     *  {@link com.smartgwt.client.data.DataSourceField#getForeignKey foreign key relation} to your <code>Customer</code> 
     *  dataSource, and your <code>Customer</code> dataSource has a "region" field.  If you wanted 
     *  to fetch all Orders for Customers in the EMEA region, you could declare criteria like 
     *  this:<pre>
     *     {fieldName: "Customer.region", operator: "equals", value: "EMEA"}
     *  </pre>
     *  This would be transformed into a subquery filter that would select only the records you 
     *  want: <pre>
     *  {
     *       fieldQuery: {
     *           dataSource: "Customer",
     *           queryOutput: "region"
     *       }, operator: "equals", value: "EMEA"
     *  }
     *  </pre>
     *  This transformation takes place before the filtering subsystem even sees the criteria, so
     *  declaring the shortcut form via <code>fieldName</code> leads to <b>exactly</b> the same 
     *  filtering behavior as if you specified the subquery directly as a <code>fieldQuery</code>
     *  <p>
     *  See the <code>AdvancedCriterionSubquery</code> overview linked above for more details of 
     *  the extremely powerful subquery filtering options.
     *
     * @param fieldName New fieldName value. Default value is null
     * @return {@link com.smartgwt.client.data.Criterion Criterion} instance, for chaining setter calls
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public Criterion setFieldName(String fieldName) {
        return (Criterion)setAttribute("fieldName", fieldName);
    }

    /**
     * Name of the field in each {@link com.smartgwt.client.data.Record} that this criterion applies to.  Not applicable for a
     *  criterion with {@link com.smartgwt.client.data.Criterion#getCriteria sub-criteria}. Can be specified as a dataPath to 
     *  allow matching nested objects. Use '/' as delimiters for dataPath. See 
     *  {@link com.smartgwt.client.docs.DataPath dataPath} for more information.
     * 
     *  <p>
     *  <h4>{@link com.smartgwt.client.data.Criterion#getFieldQuery fieldQuery} shortcuts</h4>
     *  <code>fieldName</code> can also be used to express a compact form of related-field filtering.
     *  If you set this property to the qualified name of a field on a related DataSource, it will
     *  be transformed into a basic {@link com.smartgwt.client.data.AdvancedCriterionSubquery}.  For example, say you 
     *  have an <code>Order</code> dataSource, which has a 
     *  {@link com.smartgwt.client.data.DataSourceField#getForeignKey foreign key relation} to your <code>Customer</code> 
     *  dataSource, and your <code>Customer</code> dataSource has a "region" field.  If you wanted 
     *  to fetch all Orders for Customers in the EMEA region, you could declare criteria like 
     *  this:<pre>
     *     {fieldName: "Customer.region", operator: "equals", value: "EMEA"}
     *  </pre>
     *  This would be transformed into a subquery filter that would select only the records you 
     *  want: <pre>
     *  {
     *       fieldQuery: {
     *           dataSource: "Customer",
     *           queryOutput: "region"
     *       }, operator: "equals", value: "EMEA"
     *  }
     *  </pre>
     *  This transformation takes place before the filtering subsystem even sees the criteria, so
     *  declaring the shortcut form via <code>fieldName</code> leads to <b>exactly</b> the same 
     *  filtering behavior as if you specified the subquery directly as a <code>fieldQuery</code>
     *  <p>
     *  See the <code>AdvancedCriterionSubquery</code> overview linked above for more details of 
     *  the extremely powerful subquery filtering options.
     *
     * @return Current fieldName value. Default value is null
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public String getFieldName()  {
        return getAttributeAsString("fieldName");
    }
    

    /**
     * A subquery to use instead of a {@link com.smartgwt.client.data.Criterion#getFieldName fieldName}.  When you use a 
     * <code>fieldQuery</code> instead of a <code>fieldName</code>, you are comparing the criterion {@link
     * com.smartgwt.client.data.Criterion#getValue value} to the result of running a per-record subquery, rather than a field
     * value found directly on the record.  Note, it is also possible to specify both a  <code>fieldQuery</code> and a {@link
     * com.smartgwt.client.data.Criterion#getValueQuery valueQuery}. <p> See the {@link
     * com.smartgwt.client.data.AdvancedCriterionSubquery subquery overview} for more details of the  criteria subquery
     * feature, and examples of use. <p> Note, if you specify both <code>fieldQuery</code> and <code>fieldName</code> in a
     * criterion, we use the <code>fieldName</code> and the <code>fieldQuery</code> is ignored.   <p> Note also that
     * <code>fieldName</code> supports a special shortcut syntax for declaring a  <code>fieldQuery</code> as a simple qualified
     * reference to a related field.  See the  <code>fieldName</code> doc linked above for details
     *
     * @param fieldQuery New fieldQuery value. Default value is null
     * @return {@link com.smartgwt.client.data.Criterion Criterion} instance, for chaining setter calls
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public Criterion setFieldQuery(AdvancedCriterionSubquery fieldQuery) {
        return (Criterion)setAttribute("fieldQuery", fieldQuery == null ? null : fieldQuery.getJsObj());
    }

    /**
     * A subquery to use instead of a {@link com.smartgwt.client.data.Criterion#getFieldName fieldName}.  When you use a 
     * <code>fieldQuery</code> instead of a <code>fieldName</code>, you are comparing the criterion {@link
     * com.smartgwt.client.data.Criterion#getValue value} to the result of running a per-record subquery, rather than a field
     * value found directly on the record.  Note, it is also possible to specify both a  <code>fieldQuery</code> and a {@link
     * com.smartgwt.client.data.Criterion#getValueQuery valueQuery}. <p> See the {@link
     * com.smartgwt.client.data.AdvancedCriterionSubquery subquery overview} for more details of the  criteria subquery
     * feature, and examples of use. <p> Note, if you specify both <code>fieldQuery</code> and <code>fieldName</code> in a
     * criterion, we use the <code>fieldName</code> and the <code>fieldQuery</code> is ignored.   <p> Note also that
     * <code>fieldName</code> supports a special shortcut syntax for declaring a  <code>fieldQuery</code> as a simple qualified
     * reference to a related field.  See the  <code>fieldName</code> doc linked above for details
     *
     * @return Current fieldQuery value. Default value is null
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public AdvancedCriterionSubquery getFieldQuery()  {
        return AdvancedCriterionSubquery.getOrCreateRef(getAttributeAsJavaScriptObject("fieldQuery"));
    }
    

    /**
     * Operator this criterion applies.
     *
     * @param operator New operator value. Default value is null
     * @return {@link com.smartgwt.client.data.Criterion Criterion} instance, for chaining setter calls
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public Criterion setOperator(OperatorId operator) {
        return (Criterion)setAttribute("operator", operator == null ? null : operator.getValue());
    }

    /**
     * Operator this criterion applies.
     *
     * @return Current operator value. Default value is null
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public OperatorId getOperator()  {
        return EnumUtil.getEnum(OperatorId.values(), getAttribute("operator"));
    }
    
    
    

    /**
     * Wherever {@link com.smartgwt.client.docs.DynamicCriteria} are supported, <code>valuePath</code> can be specified as a
     * path in the current {@link com.smartgwt.client.widgets.Canvas#getRuleScope Canvas.ruleScope} as an alternative to
     * setting a fixed {@link com.smartgwt.client.data.Criterion#getValue value}. <p> Note: <code>valuePath</code> vs setting a
     * path for {@link com.smartgwt.client.data.Criterion#getFieldName fieldName}: <ul> <li> use a path for
     * <code>criterion.fieldName</code> when criteria will be matched against a      nested data structure. <li> use
     * <code>criterion.valuePath</code> when the values used in filtering should be      dynamically derived based on the
     * {@link com.smartgwt.client.widgets.Canvas#getRuleScope Canvas.ruleScope}.  This does not imply that the      criteria
     * will be matched against a nested structure. </ul>
     *
     * @param valuePath New valuePath value. Default value is null
     * @return {@link com.smartgwt.client.data.Criterion Criterion} instance, for chaining setter calls
     */
    public Criterion setValuePath(String valuePath) {
        return (Criterion)setAttribute("valuePath", valuePath);
    }

    /**
     * Wherever {@link com.smartgwt.client.docs.DynamicCriteria} are supported, <code>valuePath</code> can be specified as a
     * path in the current {@link com.smartgwt.client.widgets.Canvas#getRuleScope Canvas.ruleScope} as an alternative to
     * setting a fixed {@link com.smartgwt.client.data.Criterion#getValue value}. <p> Note: <code>valuePath</code> vs setting a
     * path for {@link com.smartgwt.client.data.Criterion#getFieldName fieldName}: <ul> <li> use a path for
     * <code>criterion.fieldName</code> when criteria will be matched against a      nested data structure. <li> use
     * <code>criterion.valuePath</code> when the values used in filtering should be      dynamically derived based on the
     * {@link com.smartgwt.client.widgets.Canvas#getRuleScope Canvas.ruleScope}.  This does not imply that the      criteria
     * will be matched against a nested structure. </ul>
     *
     * @return Current valuePath value. Default value is null
     */
    public String getValuePath()  {
        return getAttributeAsString("valuePath");
    }
    

    /**
     * A subquery to use instead of a {@link com.smartgwt.client.data.Criterion#getValue value}.  When you use a 
     * <code>valueQuery</code> instead of a <code>value</code>, you are comparing the values  in the record field named in the
     * criterion {@link com.smartgwt.client.data.Criterion#getFieldName fieldName} to the result of  running a per-record
     * subquery, rather than a literal scalar value.  .  Note, it is also  possible to specify both a <code>valueQuery</code>
     * and a  {@link com.smartgwt.client.data.Criterion#getFieldQuery fieldQuery}. <p> See the {@link
     * com.smartgwt.client.data.AdvancedCriterionSubquery subquery overview} for more details of the  criteria subquery
     * feature, and examples of use. <p> Note, if you specify both <code>valueQuery</code> and <code>value</code> in a
     * criterion, we use the <code>value</code> and the <code>valueQuery</code> is ignored
     *
     * @param valueQuery New valueQuery value. Default value is null
     * @return {@link com.smartgwt.client.data.Criterion Criterion} instance, for chaining setter calls
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public Criterion setValueQuery(AdvancedCriterionSubquery valueQuery) {
        return (Criterion)setAttribute("valueQuery", valueQuery == null ? null : valueQuery.getJsObj());
    }

    /**
     * A subquery to use instead of a {@link com.smartgwt.client.data.Criterion#getValue value}.  When you use a 
     * <code>valueQuery</code> instead of a <code>value</code>, you are comparing the values  in the record field named in the
     * criterion {@link com.smartgwt.client.data.Criterion#getFieldName fieldName} to the result of  running a per-record
     * subquery, rather than a literal scalar value.  .  Note, it is also  possible to specify both a <code>valueQuery</code>
     * and a  {@link com.smartgwt.client.data.Criterion#getFieldQuery fieldQuery}. <p> See the {@link
     * com.smartgwt.client.data.AdvancedCriterionSubquery subquery overview} for more details of the  criteria subquery
     * feature, and examples of use. <p> Note, if you specify both <code>valueQuery</code> and <code>value</code> in a
     * criterion, we use the <code>value</code> and the <code>valueQuery</code> is ignored
     *
     * @return Current valueQuery value. Default value is null
     * @see com.smartgwt.client.docs.AdvancedFilter Advanced Filtering
     */
    public AdvancedCriterionSubquery getValueQuery()  {
        return AdvancedCriterionSubquery.getOrCreateRef(getAttributeAsJavaScriptObject("valueQuery"));
    }
    

    // ********************* Methods ***********************

    // ********************* Static Methods ***********************

    // ***********************************************************


	
	public static final boolean instanceOf(Object object) {
		return object instanceof Criterion;
	}
	
    public Criterion(Criterion c) {
        Object o;
        o = c.getAttributeAsObject("operator");
        if (o != null) setAttribute("operator", o);
        o = c.getAttributeAsObject("fieldName");
        if (o != null) setAttribute("fieldName", o);
        o = c.getAttributeAsObject("criteria");
        if (o != null) setAttribute("criteria", o);
        o = c.getAttributeAsObject("value");
        if (o != null) setAttribute("value", o);
    }

    public Criterion(Criterion[] criterias) {
        this(OperatorId.AND, criterias);
    }

    public Criterion(OperatorId operator, Criterion[] criterias) {
        buildCriterionFromList(operator, criterias);
    }
    
    public Criterion(OperatorId operator) {
        setAttribute("operator", operator.getValue());
    }

    public Criterion(String fieldName) {
        setAttribute("fieldName", fieldName);
    }

    public Criterion(String fieldName, OperatorId operator) {
        this(fieldName);
        setAttribute("operator", operator.getValue());
    }
    
    /**
     * Constructor for Criterion with fieldName, operator and value.
     * 
     * @param fieldName the field name
     * @param operator the operator
     * @param value the value
     */
    public Criterion(String fieldName, OperatorId operator, Integer value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }
    
    /**
     * Constructor for Criterion with fieldName, operator, start and end values. This constructor is only valid when
     * the operator is {@link OperatorId#BETWEEN} or {@link OperatorId#BETWEEN_INCLUSIVE}
     *
     * @param fieldName the field name
     * @param operator the operator
     * @param start the start value
     * @param end the end value
     */
    public Criterion(String fieldName, OperatorId operator, Integer start, Integer end) {
        this(fieldName, operator);
        assert operator == OperatorId.BETWEEN || operator == OperatorId.BETWEEN_INCLUSIVE : "The operator type must be OperatorId.BETWEEN or OperatorId.BETWEEN_INCLUSIVE";
        setAttribute("start", start);
        setAttribute("end", end);
    }

    /**
     * Constructor for Criterion with fieldName, operator and value.
     * 
     * @param fieldName the field name
     * @param operator the operator
     * @param value the value
     */
    public Criterion(String fieldName, OperatorId operator, String value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    /**
     * Constructor for Criterion with fieldName, operator, start and end values. This constructor is only valid when
     * the operator is {@link OperatorId#BETWEEN} or {@link OperatorId#BETWEEN_INCLUSIVE}
     *
     * @param fieldName the field name
     * @param operator the operator
     * @param start the start value
     * @param end the end value
     */
    public Criterion(String fieldName, OperatorId operator, String start, String end) {
        this(fieldName, operator);
        assert operator == OperatorId.BETWEEN || operator == OperatorId.BETWEEN_INCLUSIVE : "The operator type must be OperatorId.BETWEEN or OperatorId.BETWEEN_INCLUSIVE";
        setAttribute("start", start);
        setAttribute("end", end);
    }

    /**
     * Constructor for Criterion with fieldName, operator and value.
     * 
     * @param fieldName the field name
     * @param operator the operator
     * @param value the value
     */
    public Criterion(String fieldName, OperatorId operator, Float value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    /**
     * Constructor for Criterion with fieldName, operator, start and end values. This constructor is only valid when
     * the operator is {@link OperatorId#BETWEEN} or {@link OperatorId#BETWEEN_INCLUSIVE}
     *
     * @param fieldName the field name
     * @param operator the operator
     * @param start the start value
     * @param end the end value
     */
    public Criterion(String fieldName, OperatorId operator, Float start, Float end) {
        this(fieldName, operator);
        assert operator == OperatorId.BETWEEN || operator == OperatorId.BETWEEN_INCLUSIVE : "The operator type must be OperatorId.BETWEEN or OperatorId.BETWEEN_INCLUSIVE";
        setAttribute("start", start);
        setAttribute("end", end);
    }

    /**
     * Constructor for Criterion with fieldName, operator and value.
     * 
     * @param fieldName the field name
     * @param operator the operator
     * @param value the value
     */
    public Criterion(String fieldName, OperatorId operator, Date value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    /**
     * Constructor for Criterion with fieldName, operator, start and end values. This constructor is only valid when
     * the operator is {@link OperatorId#BETWEEN} or {@link OperatorId#BETWEEN_INCLUSIVE}
     *
     * @param fieldName the field name
     * @param operator the operator
     * @param start the start value
     * @param end the end value
     */
    public Criterion(String fieldName, OperatorId operator, Date start, Date end) {
        this(fieldName, operator);
        assert operator == OperatorId.BETWEEN || operator == OperatorId.BETWEEN_INCLUSIVE : "The operator type must be OperatorId.BETWEEN or OperatorId.BETWEEN_INCLUSIVE";
        setAttribute("start", start);
        setAttribute("end", end);
    }

    /**
     * Constructor for Criterion with fieldName, operator and RelativeDate value.
     * 
     * @param fieldName the field name
     * @param operator the operator
     * @param value the RelativeDate value
     */
    public Criterion(String fieldName, OperatorId operator, RelativeDate value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    /**
     * Constructor for Criterion with fieldName, operator, start and end RelativeDate values. This constructor 
     * is only valid when the operator is {@link OperatorId#BETWEEN} or {@link OperatorId#BETWEEN_INCLUSIVE}
     *
     * @param fieldName the field name
     * @param operator the operator
     * @param start the start value
     * @param end the end value
     */
    public Criterion(String fieldName, OperatorId operator, RelativeDate start, RelativeDate end) {
        this(fieldName, operator);
        assert operator == OperatorId.BETWEEN || operator == OperatorId.BETWEEN_INCLUSIVE : "The operator type must be OperatorId.BETWEEN or OperatorId.BETWEEN_INCLUSIVE";
        setAttribute("start", start);
        setAttribute("end", end);
    }

    /**
     * Constructor for Criterion with fieldName, operator and value.
     * 
     * @param fieldName the field name
     * @param operator the operator
     * @param value the value
     */
    public Criterion(String fieldName, OperatorId operator, Boolean value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    /**
     * Constructor for Criterion with fieldName, operator and value.
     * Note that JavaScript does not natively have an equivalent format to 
     * Java long (fixed point 64 bit). As such the value passed in will be
     * converted to a double value for storage.
     * @param fieldName  the field name
     * @param operator the operator
     * @param value the value
     */
    public Criterion(String fieldName, OperatorId operator, Long value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    /**
     * Constructor for Criterion with fieldName, operator, start and end values. This constructor is only valid when
     * the operator is {@link OperatorId#BETWEEN} or {@link OperatorId#BETWEEN_INCLUSIVE}
     *
     * @param fieldName the field name
     * @param operator the operator
     * @param start the start value
     * @param end the end value
     */
    public Criterion(String fieldName, OperatorId operator, Long start, Long end) {
        this(fieldName, operator);
        assert operator == OperatorId.BETWEEN || operator == OperatorId.BETWEEN_INCLUSIVE : "The operator type must be OperatorId.BETWEEN or OperatorId.BETWEEN_INCLUSIVE";
        setAttribute("start", start);
        setAttribute("end", end);
    }

    public Criterion(String fieldName, OperatorId operator, Integer[] value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }
    
    public Criterion(String fieldName, OperatorId operator, String[] value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    public Criterion(String fieldName, OperatorId operator, Float[] value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }
    
    public Criterion(String fieldName, OperatorId operator, Long[] value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }


    public Criterion(String fieldName, OperatorId operator, Date[] value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    public Criterion(String fieldName, OperatorId operator, Boolean[] value) {
        this(fieldName, operator);
        setAttribute("value", value);
    }

    /**
     * Adds a new criteria.
     * <p>If the present criteria operation is "and", 
     * the new criteria is appended to the criteria list.
     * <p>Otherwise, the existing criteria is replaced with an "and" criteria, 
     * with two sub-criteria: the existing criteria, and the passed criteria.
     * <p>Note that if the existing criteria was empty 
     * (as would happen if new AdvancedCriteria() were called with no arguments), 
     * an "and" operator will still be introduced, but the passed criteria will be 
     * the only sub-criteria.
     * <p>To add an additional Criterion when using the "or" operator, use 
     * {@link #appendToCriterionList}.
     *
     * @param c the passed criteria object
     */
    public void addCriteria(Criterion c) {
        String opString = this.getAttributeAsString("operator");
        if (opString != null && opString.equals(OperatorId.AND.getValue())) {
            appendToCriterionList(c);
        } else {
	         if (opString != null) {
                Criterion thisCopy = new Criterion(this);
                JSOHelper.deleteAttributeIfExists(jsObj, "fieldName");
                JSOHelper.deleteAttributeIfExists(jsObj, "value");
                Criterion[] criteriaList = { thisCopy, c };
                buildCriterionFromList(OperatorId.AND, criteriaList);                        
            } else {
                buildCriterionFromList(OperatorId.AND, new Criterion[] {c});
            }
        }
    }

    /**
     * Adds a new criteria.
     * If the passed criteria is a simple Criteria or Criterion, it
     * will turn into AdvancedCriteria, and will be added to the existent criteria.
     *
     * @param c the passed criteria object
     */
    public void addCriteria(Criteria c) {
        if (!c.isAdvanced() && !(c instanceof AdvancedCriteria)) {
            addCriteria(c.asAdvancedCriteria());
        } else {
            addCriteria((AdvancedCriteria)c);
        }
    }

    /**
     * This method calls {@link #addCriteria(String, OperatorId, value)}, passing "EQUALS" as the OperatorId and
     * Boolean.TRUE as value.
     * @see #addCriteria(Criterion)
     */
    public void addCriteria(String field) {
        addCriteria(field, OperatorId.EQUALS, Boolean.TRUE);
    }
    /**
     * This method calls {@link #addCriteria(String, OperatorId, String)}, passing "EQUALS" as the OperatorId.
     * @see #addCriteria(Criterion)
     */
    public void addCriteria(String field, String value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, Integer value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, Float value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, Date value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, Boolean value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, String[] value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, Integer[] value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, Float[] value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, Date[] value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    /**
     * This method is similar to {@link #addCriteria(String, String)}.
     */
    public void addCriteria(String field, Boolean[] value) {
        addCriteria(field, OperatorId.EQUALS, value);
    }
    
    /**
     * Creates a Criterion and calls {@link #addCriteria(Criterion)}.
     */
    public void addCriteria(String field, OperatorId op, String value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */
    public void addCriteria(String field, OperatorId op, Integer value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */
    public void addCriteria(String field, OperatorId op, Float value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */
    public void addCriteria(String field, OperatorId op, Date value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */
    public void addCriteria(String field, OperatorId op, Boolean value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */ 
    public void addCriteria(String field, OperatorId op, String[] value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */
    public void addCriteria(String field, OperatorId op, Integer[] value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */
    public void addCriteria(String field, OperatorId op, Float[] value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */
    public void addCriteria(String field, OperatorId op, Date[] value) {
        addCriteria(new Criterion(field, op, value));
    }
    /**
     * This method is similar to {@link #addCriteria(String, OperatorId, String)}.
     */
    public void addCriteria(String field, OperatorId op, Boolean[] value) {
        addCriteria(new Criterion(field, op, value));
    }

    public void buildCriterionFromList(OperatorId operator, Criterion[] criterias) {
        setAttribute("operator", operator.getValue());
        setAttribute("criteria", JSOHelper.createJavaScriptArray());
        for (int i = 0; i < criterias.length; i++) appendToCriterionList(criterias[i]);
    }
    
    /**
     * For a Criterion that uses {@link OperatorId.AND} or {@link OperatorId.OR}, add an
     * additional Criterion to the list of subcriteria to be evaluated.
     */
    public void appendToCriterionList(Criterion c) {
        if (c == null) return;
        c.unmarkAdvancedCriteria();
        
        JavaScriptObject listJS = getAttributeAsJavaScriptObject("criteria");
        if (!JSOHelper.isArray(listJS)) {
            String opString = this.getAttributeAsString("operator");
            if ((opString == null) || 
            	 (!opString.equalsIgnoreCase((OperatorId.AND).getValue()) && 
            	  !opString.equalsIgnoreCase((OperatorId.OR).getValue()))) 
            {
                throw new IllegalArgumentException("appendToCriterionList() may only be used with \"and\" or \"or\" operator");
            }
            setAttribute("criteria", JSOHelper.createJavaScriptArray());
            listJS = getAttributeAsJavaScriptObject("criteria");
        }
    
        JSOHelper.setArrayValue(listJS, JSOHelper.getArrayLength(listJS), c.getJsObj());
    }
    
    /**
     * Specifies a "custom" operator to this criterion.
     * @param operator the operator
     */
    public void setCustomOperator(String operator) {
        setAttribute("operator", operator);
    }
    
    public void markAdvancedCriteria() {
        setAttribute("_constructor", "AdvancedCriteria");
    }
    
    public void unmarkAdvancedCriteria() {
        JSOHelper.deleteAttributeIfExists(jsObj, "_constructor");
    }

    /**
     * Retrieves the specified value for this criterion. Only applies to criterion where operator and value have
     * been specified - will not apply to criterion containing {@link #getCriteria(),sub criteria}.
     * @return
     */
    public String getValueAsString() {
        return JSOHelper.getAttribute(getJsObj(), "value");
    }
    /**
     * Synonym of {@link #getValueAsInteger()}.
     */
    public final Integer getValueAsInt() {
        return getValueAsInteger();
    }
    /**
     * Retrieves the specified value for this criterion. Only applies to criterion where operator and value have
     * been specified - will not apply to criterion containing {@link #getCriteria(),sub criteria}.
     * @return
     */
    public Integer getValueAsInteger() {
        return JSOHelper.getAttributeAsInt(getJsObj(), "value");
    }
    /**
     * Retrieves the specified value for this criterion. Only applies to criterion where operator and value have
     * been specified - will not apply to criterion containing {@link #getCriteria(),sub criteria}.
     * @return
     */
    public Float getValueAsFloat() {
        return JSOHelper.getAttributeAsFloat(getJsObj(), "value");
    }
    /**
     * Retrieves the specified value for this criterion. Only applies to criterion where operator and value have
     * been specified - will not apply to criterion containing {@link #getCriteria(),sub criteria}.
     * @return
     */
    public Date getValueAsDate() {
        return JSOHelper.getAttributeAsDate(getJsObj(), "value");
    }
    /**
     * Retrieves the specified value for this criterion. Only applies to criterion where operator and value have
     * been specified - will not apply to criterion containing {@link #getCriteria(),sub criteria}.
     * @return
     */
    public Boolean getValueAsBoolean() {
        return JSOHelper.getAttributeAsBoolean(getJsObj(), "value");
    }
    /**
     * Retrieves the specified value for this criterion. Only applies to criterion where operator and value have
     * been specified - will not apply to criterion containing {@link #getCriteria(),sub criteria}.
     * @return
     */
    public String[] getValueAsStringArray() {
        return JSOHelper.getAttributeAsStringArray(getJsObj(), "value");
    }
    /**
     * Retrieves the specified value for this criterion. Only applies to criterion where operator and value have
     * been specified - will not apply to criterion containing {@link #getCriteria(),sub criteria}.
     * @return
     */
    public int[] getValueAsIntArray() {
        return JSOHelper.getAttributeAsIntArray(getJsObj(), "value");
    }
    /**
     * Returns an AdvancedCriteria derived from this Criteria object.
     * @return
     */
    public AdvancedCriteria asAdvancedCriteria() {
        return new AdvancedCriteria(this);
    }

}
