/*
 * (C) Copyright IBM Corp. 1997-1998  All rights reserved.
 *
 * US Government Users Restricted Rights Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * The program is provided "as is" without any warranty express or
 * implied, including the warranty of non-infringement and the implied
 * warranties of merchantibility and fitness for a particular purpose.
 * IBM will not be liable for any damages suffered by you as a result
 * of using the Program. In no event will IBM be liable for any
 * special, indirect or consequential damages or lost profits even if
 * IBM has been advised of the possibility of their occurrence. IBM
 * will not be liable for any third party claims against you.
 */

package com.ibm.xml.parser;

import java.util.*;

/**
 * CM2op provides content model support for content model nodes that contain the &quot;|&quot;,
 * or &quot;,&quot; language primitives.  Refer to ElementDecl for an overview
 * of the content model, and how these language primitives are used to express relationships.
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 * @see com.ibm.xml.parser.ElementDecl
 * @see com.ibm.xml.parser.CMNode
 * @see com.ibm.xml.parser.CMLeaf
 */
public class CM2op extends CMNode {
    
            int     type        =   0;      // ',' or `|'
            CMNode  leftNode    =   null;
            CMNode  rightNode   =   null;
    
    /**
     * Constructor.
     * @param type      The language primitive associated with this content model node.  Must
     *                  be &quot;|&quot;, or &quot;,&quot;.
     * @param leftNode  The content model node prior to the <var>type</var> separator, or <var>null</var>
     *                  if <var>left</var> will be set later.
     * @param rightNode The content model node after the <var>type</var> separator, or <var>null</var>
     *                  if <var>right</var> will be set later.
     */
    public CM2op(int type, CMNode leftNode, CMNode rightNode) {
        this.type       = type;
        this.leftNode   = leftNode;
        this.rightNode  = rightNode;
    }
    
    /**
     * Returns the language primitive associated with this content model node.  
     * @return          &quot;|&quot;, or &quot;,&quot; (should never be <var>null</var>). 
     */
    public int getType() {
        return this.type;
    }
    
    /**
     * Returns the content model node prior to the separator <var>type</var>.  
     * @return          The content model node prior to the separator <var>type</var>, or 
     *                  <var>null</var> if no node currently exists.
     * @see #setLeft
     */
    public CMNode getLeft() {
        return this.leftNode;
    }
    
    /**
     * Sets the content model node prior to the separator <var>type</var>.  
     * @param   leftNode    The content model node prior to the separator <var>type</var>.
     * @see #getLeft
     */
    public void setLeft(CMNode leftNode) {
        this.leftNode = leftNode;
    }
    
    /**
     * Returns the content model node after the separator <var>type</var>.  
     * @return          The content model node after the separator <var>type</var>, or 
     *                  <var>null</var> if no node currently exists.
     * @see #setRight
     */
    public CMNode getRight() {
        return this.rightNode;
    }
    
    /**
     * Sets the content model node after the separator <var>type</var>.  
     * @param   rightNode   The content model node after the separator <var>type</var>.
     * @see #getRight
     */
    public void setRight(CMNode rightNode) {
        this.rightNode = rightNode;
    }
    
    /**
     * Returns the string representation of this left and right content model nodes and its 
     * associated separator language primitive.
     * Example: <code>(<var>elementX</var>|<var>elementY</var>)</code>
     * @return          The string representation of this left and right content model nodes
     *                  and its separator (should never be <var>null</var>). 
     */
    public String toString() {
        if (this.leftNode instanceof CM2op && ((CM2op)this.leftNode).type == this.type) {
            return "("+((CM2op)this.leftNode).toStringWithoutParen()+(char)+this.type+this.rightNode.toString()+")";
        } else {
            return "("+this.leftNode.toString()+(char)this.type+this.rightNode.toString()+")";
        }
    }
    
    String toStringWithoutParen() {
        if (this.leftNode instanceof CM2op && ((CM2op)this.leftNode).type == this.type) {
            return ((CM2op)this.leftNode).toStringWithoutParen()+(char)+this.type+this.rightNode.toString();
        } else {            
            return this.leftNode.toString()+(char)+this.type+this.rightNode.toString();
        }
    }
    
    CMNode cloneNode() {
        return new CM2op(this.type, this.leftNode.cloneNode(), this.rightNode.cloneNode());
    }

    boolean nullable() {
        if (null == this.nullable) {
            switch (this.type) {
              case ',':
                this.nullable = new Boolean(this.leftNode.nullable() && this.rightNode.nullable());
                break;
              case '|':
                this.nullable = new Boolean(this.leftNode.nullable() || this.rightNode.nullable());
                break;
            }
        }
        return this.nullable.booleanValue();
    }
    
    BitSet firstpos() {
        if (null == this.firstPos) {
            if ('|' == this.type) {
                this.firstPos = (BitSet)this.leftNode.firstpos().clone();
                this.firstPos.or(this.rightNode.firstpos());
            } else {
                if (this.leftNode.nullable()) {
                    this.firstPos = (BitSet)this.leftNode.firstpos().clone();
                    this.firstPos.or(this.rightNode.firstpos());
                } else
                    this.firstPos = this.leftNode.firstpos();
            }
        }
        return this.firstPos;
    }
    
    BitSet lastpos() {
        if (null == this.lastPos) {
            if ('|' == this.type) {
                this.lastPos = (BitSet)this.leftNode.lastpos().clone();
                this.lastPos.or(this.rightNode.lastpos());
            } else {
                if (this.rightNode.nullable()) {
                    this.lastPos = (BitSet)this.leftNode.lastpos().clone();
                    this.lastPos.or(this.rightNode.lastpos());
                } else
                    this.lastPos = this.rightNode.lastpos();
            }
        }
        return this.lastPos;
    }
    
    void prepare(int n) {
        this.leftNode.prepare(n);
        this.rightNode.prepare(n);
    }
    
    void setFollowpos(BitSet[] fp) {
        this.leftNode.setFollowpos(fp);
        this.rightNode.setFollowpos(fp);
        if ('|' == this.type) {
        } else if (',' == this.type) {
            for (int i = 0;  i < fp.length;  i ++) {
                if (this.leftNode.lastpos().get(i))
                    fp[i].or(this.rightNode.firstpos());
            }
        }
    }
}

