/*
 * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jsp.java;

import com.caucho.jsp.JspParseException;
import com.caucho.jsp.cfg.TldAttribute;
import com.caucho.jsp.cfg.TldVariable;
import com.caucho.util.L10N;
import com.caucho.vfs.WriteStream;
import com.caucho.xml.QName;

import java.io.IOException;
import java.util.ArrayList;

public class JspDirectiveVariable extends JspNode {
  private static final QName NAME_GIVEN = new QName("name-given");
  private static final QName NAME_FROM_ATTRIBUTE =
    new QName("name-from-attribute");
  private static final QName ALIAS = new QName("alias");
  private static final QName VARIABLE_CLASS = new QName("variable-class");
  private static final QName DECLARE = new QName("declare");
  private static final QName SCOPE = new QName("scope");
  private static final QName DESCRIPTION = new QName("description");
  
  static final L10N L = new L10N(JspDirectiveVariable.class);

  private String _nameGiven;
  private String _nameFromAttribute;
  private String _alias;
  private String _variableClass;
  private boolean _isDeclare = true;
  private String _scope;
  private String _description;
  
  /**
   * Adds an attribute.
   *
   * @param name the attribute name
   * @param value the attribute value
   */
  public void addAttribute(QName name, String value)
    throws JspParseException
  {
    if (! _gen.getParseState().isTag())
      throw error(L.l("'{0}' is only allowed in .tag files.  Attribute directives are not allowed in normal JSP files.",
                      getTagName()));
    
    JavaTagGenerator gen = (JavaTagGenerator) _gen;
    if (NAME_GIVEN.equals(name)) {
      if (gen.findVariable(value) != null) {
	throw error(L.l("@variable name-given '{0}' is already used by another variable.",
			value));
      }
      else if (gen.findAttribute(value) != null) {
	throw error(L.l("@variable name-given '{0}' is already used by an attribute.",
			value));
      }
      else if (value.equals(gen.getDynamicAttributes())) {
	throw error(L.l("@variable name-given '{0}' cannot be the same as the tag's dynamic-attributes.",
			value));
      }
      
      _nameGiven = value;
    }
    else if (NAME_FROM_ATTRIBUTE.equals(name)) {
      if (gen.findVariable(value) != null) {
	throw error(L.l("@variable name-from-attribute '{0}' is already used by another variable.",
			value));
      }
      /*
      else if (gen.findAttribute(value) != null) {
	throw error(L.l("@variable name-from-attribute '{0}' is already used by an attribute.",
			value));
      }
      */
      
      _nameFromAttribute = value;
    }
    else if (ALIAS.equals(name))
      _alias = value;
    else if (VARIABLE_CLASS.equals(name))
      _variableClass = value;
    else if (DECLARE.equals(name))
      _isDeclare = attributeToBoolean(name.getName(), value);
    else if (SCOPE.equals(name)) {
      if (! "NESTED".equals(value) &&
	  ! "AT_BEGIN".equals(value) &&
	  ! "AT_END".equals(value))
	throw error(L.l("'{0}' is an illegal scope value.  NESTED, AT_BEGIN, and AT_END are the only accepted values.",
			value));

      _scope = value;
    }
    else if (DESCRIPTION.equals(name))
      _description = value;
    else {
      throw error(L.l("'{0}' is an unknown JSP variable directive attributes.  Valid attributes are: alias, declare, description, name-from-attribute, name-given, scope, variable-class.",
                      name.getName()));
    }
  }

  /**
   * When the element complets.
   */
  public void endElement()
    throws JspParseException
  {
    if (! _gen.getParseState().isTag())
      throw error(L.l("'{0}' is only allowed in .tag files.  Variable directives are not allowed in normal JSP files.",
                      getTagName()));
    
    if (_nameGiven == null && _nameFromAttribute == null)
      throw error(L.l("<{0}> needs a 'name-given' or 'name-from-attribute' attribute.",
                      getTagName()));

    if (_nameFromAttribute != null && _alias == null)
      throw error(L.l("<{0}> needs an 'alias' attribute.  name-from-attribute requires an alias attribute.",
                      getTagName()));
    if (_alias != null && _nameFromAttribute == null)
      throw error(L.l("<{0}> needs an 'name-from-attribute' attribute.  alias requires a name-from-attribute attribute.",
                      getTagName()));

    JavaTagGenerator tagGen = (JavaTagGenerator) _gen;

    TldVariable var = new TldVariable();
    var.setNameGiven(_nameGiven);
    var.setNameFromAttribute(_nameFromAttribute);
    var.setAlias(_alias);

    String name = _nameGiven;
    if (name == null)
      name = _nameFromAttribute;

    if (_variableClass != null)
      var.setVariableClass(_variableClass);

    var.setDeclare(_isDeclare);
    if (_scope != null)
      var.setScope(_scope);

    tagGen.addVariable(var);
  }
  
  /**
   * Return true if the node only has static text.
   */
  public boolean isStatic()
  {
    return true;
  }

  /**
   * Generates the XML text representation for the tag validation.
   *
   * @param os write stream to the generated XML.
   */
  public void printXml(WriteStream os)
    throws IOException
  {
    os.print("<jsp:directive.variable");
    os.print(" jsp:id=\"" + _gen.generateJspId() + "\"");

    if (_nameGiven != null)
      os.print(" name-given=\"" + _nameGiven + "\"");

    if (_nameFromAttribute != null)
      os.print(" name-from-attribute=\"" + _nameFromAttribute + "\"");

    if (_variableClass != null)
      os.print(" variable-class=\"" + _variableClass + "\"");

    os.print("/>");
  }

  /**
   * Generates the code for the tag
   *
   * @param out the output writer for the generated java.
   */
  public void generatePrologue(JspJavaWriter out)
    throws Exception
  {
    JavaTagGenerator gen = (JavaTagGenerator) _gen;

    if (_nameFromAttribute == null)
      return;

    ArrayList<TldAttribute> attributes = gen.getAttributes();
    for (int i = 0; i < attributes.size(); i++) {
      TldAttribute attr = attributes.get(i);

      if (! attr.getName().equals(_nameFromAttribute))
	continue;

      if (! String.class.equals(attr.getType()))
	throw error(L.l("name-from-attribute variable '{0}' needs a matching String attribute, not '{1}' .  name-from-attribute requires a matching String attribute.",
			_nameFromAttribute, attr.getType().getName()));

      return;
    }
    
    throw error(L.l("name-from-attribute variable '{0}' needs a matching String attribute.",
		    _nameFromAttribute));
  }

  /**
   * Generates the code for the tag
   *
   * @param out the output writer for the generated java.
   */
  public void generate(JspJavaWriter out)
    throws Exception
  {
  }
}
