/*
 * (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.xpointer;

import com.ibm.xml.parser.TXAttribute;
import com.ibm.xml.parser.TXElement;
import java.util.Enumeration;
import java.util.Vector;
import org.w3c.dom.Attribute;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.PI;
import org.w3c.dom.Text;


/**
 * (Based on <a href="http://www.w3.org/TR/1998/WD-xptr-19980303">WD-xptr-19980303</a>)
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 */

public class RelTerm implements OtherTerm, java.io.Serializable {
    int type;
                                                // Arguments
    boolean isall = false;
    int instance;
    int nodetype;                               // XPointer.NT_*;
    String name;                                // An element name if nodetype is NT_NAME
                                         
    Vector attributes;                          // Attribute

    /**
     *
     * @param type <code>XPointer.ST_NONE / XPointer.ST_CHILD / XPointer.ST_DESCENDANT / XPointer.ST_ANCESTOR / XPointer.ST_PRECEDING / XPointer.ST_PSIBLING / XPointer.ST_FOLLOWING / XPointer.ST_FSIBLING</code>
     */
    public RelTerm(int type, boolean allp, int instance, int nodetype, String name, Vector attrs) {
        this.type = type;
        this.isall = allp;
        this.instance = instance;
        this.nodetype = nodetype;
        this.name = name;
        this.attributes = attrs;
    }

    /**
     *
     */
    public RelTerm(int type, boolean allp, int instance, int nodetype) {
        this(type, allp, instance, nodetype, null, new Vector());
    }
    /**
     *
     */
    public RelTerm(int type, boolean allp, int instance, String name) {
        this(type, allp, instance, XPointer.NT_NAME, name, new Vector());
    }

    /**
     *
     */
    public int getType() {
        return this.type;
    }

    /**
     *
     */
    public void setType(int type) {
        this.type = type;
    }

    /**
     *
     */
    public String getTypeName() {
        return this.type == XPointer.ST_NONE ? null : XPointer.literals[this.type];
    }

    
    /**
     *
     */
    public boolean isAll() {
        return this.isall;
    }

    /**
     *
     */
    public int getInstance() {
        return this.instance;
    }

    /**
     *
     */
    public int getNodeType() {
        return this.nodetype;
    }

    /**
     * Return a <code>Vector</code> of <code>RelTermAttribute</code>
     */
    public Vector getAttributesVector() {
        return this.attributes;
    }

    /**
     *
     */
    public String toString() {
        StringBuffer sb = new StringBuffer(48);
        if (this.type != XPointer.ST_NONE)  sb.append(XPointer.literals[this.type]);

        sb.append("(");
        if (this.isall) {
            sb.append(XPointer.S_ALL);
        } else
            sb.append(this.instance);
        if (this.nodetype != XPointer.NT_NONE) {
            sb.append(",");
            sb.append(this.nodetype == XPointer.NT_NAME
                      ? this.name : XPointer.nodetypes[this.nodetype]);
            for (Enumeration en = this.attributes.elements();  en.hasMoreElements();  )
                sb.append(en.nextElement().toString());
        }
        sb.append(")");
        return sb.toString();
    }

    /**
     * Check if ch is matched with this <CODE>RelTerm</CODE>
     * without <CODE>Keyword</CODE> and <CODE>InstanceOrAll</CODE> conditions.
     */
    public boolean match(Node ch) {
        boolean ret = false;
        switch (this.nodetype) {
          case XPointer.NT_NAME:
            if (!(ch instanceof Element)) break;
            ret = ((Element)ch).getTagName().equals(this.name);
            break;
          case XPointer.NT_NONE:
          case XPointer.NT_ELEMENT:
            ret = ch instanceof Element;
            break;
          case XPointer.NT_PI:
            ret = ch instanceof PI;
            break;
          case XPointer.NT_COMMENT:
            ret = ch instanceof Comment;
            break;
          case XPointer.NT_TEXT:
            ret = ch instanceof Text;
            break;
          case XPointer.NT_CDATA:
            ret = ch instanceof CDATASection;
            break;
          case XPointer.NT_ALL:
            ret = true;
            break;
        }
        if (!ret)  return ret;

        if (this.attributes.size() == 0)
            return true;
        if (!(ch instanceof Element))
            return false;

        Element el = (Element)ch;
                                                // ret is true;
        for (Enumeration en = this.attributes.elements();  en.hasMoreElements();  ) {
            RelTermAttribute rta = (RelTermAttribute)en.nextElement();
            String name = rta.getName();
            if (name.equals("*")) {
                Enumeration aen = ((TXElement)el).attributeElements();
                switch (rta.getValueType()) {
                  case XPointer.T_IMPLIED:
                    if (aen.hasMoreElements())
                        return false;
                    break;
                  case XPointer.T_ANY:
                    if (!aen.hasMoreElements())
                        return false;
                    break;
                  case XPointer.T_NAME:
                    ret = false;
                    while (aen.hasMoreElements()) {
                        if (((Attribute)aen.nextElement()).getValue().equalsIgnoreCase(rta.getValue())) {
                            ret = true;
                            break;
                        }
                    }
                    if (!ret)  return false;
                    break;

                  case XPointer.T_EXACT:
                    ret = false;
                    while (aen.hasMoreElements()) {
                        if (((Attribute)aen.nextElement()).getValue().equals(rta.getValue())) {
                            ret = true;
                            break;
                        }
                    }
                    if (!ret)  return false;
                    break;
                }
            } else {
                Enumeration aen = ((TXElement)el).attributeElements();
                Attribute attr = null;
                while (aen.hasMoreElements()) {
                    Attribute a = (Attribute)aen.nextElement();
                    if (a.getName().equals(name)) {
                        attr = a;
                        break;
                    }
                }
                switch (rta.getValueType()) {
                  case XPointer.T_IMPLIED:
                    ret = attr == null;
                    break;
                  case XPointer.T_ANY:
                    ret = attr != null;
                    break;
                  case XPointer.T_NAME:
                    ret = attr != null && attr.getValue().equalsIgnoreCase(rta.getValue());
                    break;
                  case XPointer.T_EXACT:
                    ret = attr != null && attr.getValue().equals(rta.getValue());
                    break;
                }
                if (!ret)  return false;
            }
        }
        return ret;
    }
}
