/*
 * @(#)$Id: ParserTable.java 1087 2001-08-18 01:37:50Z Bear $
 *
 * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * This software is the proprietary information of Sun Microsystems, Inc.  
 * Use is subject to license terms.
 * 
 */
package com.sun.tahiti.compiler.ll;

import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import com.sun.msv.grammar.Expression;
import com.sun.tahiti.compiler.XMLWriter;
import com.sun.tahiti.compiler.Symbolizer;

/**
 * parser table.
 * 
 * A parser table is conceptually a two dimensional array, whose cell is a
 * set of Rule objects.
 * 
 * @author
 *	<a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
 */
public class ParserTable
{
	/**
	 * actual storage.
	 * type:  Non-terminal -> ( terminal -> set(Rule) )
	 */
	private final Map impl = new HashMap();
	
	/** add a new rule. */
	public void addRule( Expression nonTerminal, Expression terminal, Rule rule ) {
		Set s = getOrCreateRuleSet(nonTerminal,terminal);
		s.add(rule);
	}
	
	/** add a new rule. */
	public void addRules( Expression nonTerminal, Set terminals, Rule rule ) {
		// TODO: could be better implemented
		Iterator itr = terminals.iterator();
		while( itr.hasNext() )
			getOrCreateRuleSet( nonTerminal, (Expression)itr.next() ).add(rule);
	}
	
	private Set getOrCreateRuleSet( Expression nonTerminal, Expression terminal ) {
		Map m = (Map)impl.get(nonTerminal);
		if(m==null)
			impl.put(nonTerminal,m=new HashMap());
		Set s = (Set)m.get(terminal);
		if(s==null)
			m.put(terminal,s=new HashSet());
		return s;
	}
	
	/**
	 * writes the contents of the table as XML.
	 */
	public void write( XMLWriter writer, Symbolizer symbolizer ) {
		writer.start("parserTable");
		int no = 0;
		
		Iterator itr = impl.keySet().iterator();
		while( itr.hasNext() ) {
			Expression nonTerm = (Expression)itr.next();
			Map t2r = (Map)impl.get(nonTerm);
			
			writer.start("action",
				new String[]{"stackTop",symbolizer.getId(nonTerm)});
			
			Rule defaultAction=null;
			{
				Set defaultActionSet = (Set)t2r.get(Expression.epsilon);
				if(defaultActionSet!=null) {
					if(defaultActionSet.size()>1)
						// only one default action is possible
						throw new Error();
					if(defaultActionSet.size()==1)
						defaultAction = (Rule)defaultActionSet.toArray()[0];
				}
			}
			
			
			Iterator jtr = t2r.keySet().iterator();
			while( jtr.hasNext() ) {
				Expression term = (Expression)jtr.next();
				// applicable rules.
				Rule[] rules = (Rule[])((Set)t2r.get(term)).toArray(new Rule[0]);
				
				if( rules.length==1 && rules[0]==defaultAction && term!=Expression.epsilon )
					continue;	// this token statement can be absorbed by the "otherwise" clause.
				
				if( term!=Expression.epsilon )
					writer.start( "token",
						new String[]{
							"id",	symbolizer.getId(term),
							"no",	Integer.toString(no++)});
				else
					writer.start( "otherwise" );
				
					
				
				for( int i=0; i<rules.length; i++ ) {
					writer.element("rule",
						new String[]{"no",symbolizer.getId(rules[i])});
				}
					
				if( term!=Expression.epsilon )
					writer.end("token");
				else
					writer.end("otherwise");
			}
			
			writer.end("action");
		}

		writer.end("parserTable");
	}
}
