package com.jclark.xsl.expr;

import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;

import com.jclark.xsl.om.*;

public class PatternList {
  private Hashtable nameRules = new Hashtable();
  private Vector typeRules[] = new Vector[Node.N_TYPES];
  private Vector anyNameRules = new Vector();

  public PatternList() {
    for (int i = 0; i < Node.N_TYPES; i++)
      typeRules[i] = new Vector();
  }

  public Object get(Node node, ExprContext context) throws XSLException {
    Vector v = getVector(node);
    int len = v.size();
    for (int i = 0; i < len; i += 2)
      if (((PathPattern)v.elementAt(i)).matches(node, context))
	return v.elementAt(i + 1);
    return null;
  }

  private static class MatchEnumeration implements Enumeration {
    private Enumeration possibleMatches;
    private Node node;
    private Object nextMatch;
    private ExprContext context;

    MatchEnumeration(Enumeration possibleMatches, Node node, ExprContext context) {
      this.node = node;
      this.possibleMatches = possibleMatches;
      this.context = context;
      setNextMatch();
    }

    public boolean hasMoreElements() {
      return nextMatch != null;
    }
    
    public Object nextElement() {
      Object tem = nextMatch;
      setNextMatch();
      return tem;
    }

    void setNextMatch() {
      while (possibleMatches.hasMoreElements()) {
	PathPattern pp = (PathPattern)possibleMatches.nextElement();
	try {
	  if (pp.matches(node, context)) {
	    nextMatch = possibleMatches.nextElement();
	  return;
	  }
	}
	catch (XSLException e) { } // FIXME
	possibleMatches.nextElement();
      }
      nextMatch = null;
    }
  }

  public Enumeration getAll(Node node, ExprContext context) {
    return new MatchEnumeration(getVector(node).elements(), node, context);
  }

  private Vector getVector(Node node) {
    Name nodeName = node.getName();
    if (nodeName != null) {
      Vector rules = (Vector)nameRules.get(nodeName);
      if (rules != null)
	return rules;
    }
    return typeRules[node.getType()];
  }

  private static void append(Vector v, PathPattern pp, Object obj) {
    v.addElement(pp);
    v.addElement(obj);
  }

  public void add(PathPattern pp, Object obj) {
    PathPatternBase ppb = (PathPatternBase)pp;
    Name matchName = ppb.getMatchName();
    if (matchName == null) {
      byte matchNodeType = ppb.getMatchNodeType();
      append(typeRules[matchNodeType], ppb, obj);
      switch (matchNodeType) {
      case Node.ELEMENT:
      case Node.ATTRIBUTE:
      case Node.PROCESSING_INSTRUCTION:
	for (Enumeration enum = nameRules.elements(); enum.hasMoreElements();)
	  append((Vector)enum.nextElement(), ppb, obj);
	append(anyNameRules, ppb, obj);
	break;
      }
    }
    else {
      Vector v = (Vector)nameRules.get(matchName);
      if (v == null) {
	v = (Vector)anyNameRules.clone();
	nameRules.put(matchName, v);
      }
      append(v, ppb, obj);
    }
  }
}
