/FileExtraction/Java_unseen/dlitz_resin/modules/jsf/src/javax/faces/webapp/UIComponentClassicTagBase.java
/*
 * Copyright (c) 1998-2011 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 version 2
 * as published by the Free Software Foundation.
 *
 * 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 javax.faces.webapp;

import java.io.*;

import java.util.*;
import java.util.logging.Logger;

import javax.faces.application.*;
import javax.faces.component.*;
import javax.faces.component.html.*;
import javax.faces.context.*;

import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import javax.servlet.ServletRequest;

public abstract class UIComponentClassicTagBase
  extends UIComponentTagBase
  implements JspIdConsumer, BodyTag
{
  protected static final String UNIQUE_ID_PREFIX = "j_id_";
  
  protected static Logger log
    = Logger.getLogger(UIComponentClassicTagBase.class.getName());


  private String _id;
  private String _jspId;

  protected PageContext pageContext;
  protected BodyContent bodyContent;

  private FacesContext _facesContext;

  private UIComponentClassicTagBase _parentUIComponentTag;

  private Tag _parent;

  private UIComponent _component;
  private boolean _created;

  
  public UIComponentClassicTagBase()
  {
    _facesContext = FacesContext.getCurrentInstance();
  }

  protected String getFacetName()
  {
    return null;
  }

  public void setJspId(String id)
  {
    _jspId = id;
  }

  public String getJspId()
  {
    return _jspId;
  }

  public void setPageContext(PageContext pageContext)
  {
    this.pageContext = pageContext;
  }

  public Tag getParent()
  {
    return _parent;
  }

  public void setParent(Tag parent)
  {
    _parent = parent;
  }

  public void setBodyContent(BodyContent bodyContent)
  {
    this.bodyContent = bodyContent;
  }

  public BodyContent getBodyContent()
  {
    return this.bodyContent;
  }

  public JspWriter getPreviousOut()
  {
    if (bodyContent != null)
      return bodyContent.getEnclosingWriter();
    else
      return pageContext.getOut();
  }

  public void setId(String id)
  {
    if (id.startsWith(UNIQUE_ID_PREFIX))
      throw new IllegalArgumentException("id may not begin with " +
                                         UNIQUE_ID_PREFIX);

    _id = id;
  }

  protected String getFacesJspId(){
    if (true) throw new UnsupportedOperationException("unimplemented");

    return null;
  }

  protected String getId()
  {
    return _id;
  }

  protected abstract boolean hasBinding();

  public UIComponent getComponentInstance()
  {
    return _component;
  }

  public boolean getCreated()
  {
    return _created;
  }

  public int doStartTag()
    throws JspException
  {
    PageContext pageContext = this.pageContext;

    this.bodyContent = null;
    _created = false;
    _component = null;

    _parentUIComponentTag
      = getParentUIComponentClassicTagBase(pageContext);

    ServletRequest request = pageContext.getRequest();

    Map tagMap = (Map) request.getAttribute("caucho.jsf.tag.map");

    if (tagMap == null) {
      tagMap = new HashMap();
      request.setAttribute("caucho.jsf.tag.map", tagMap);
    }

    Integer iterCounter = (Integer) tagMap.get(_jspId);

    iterCounter = (iterCounter == null
                   ? new Integer(0)
                   : new Integer(iterCounter.intValue() + 1));

    tagMap.put(_jspId, iterCounter);

    _component = findComponent(_facesContext);

    request.setAttribute("caucho.jsf.parent", this);

    return getDoStartValue();
  }

  /**
   * Returns the doStart value for the tag.  Defaults to EVAL_BODY_BUFFERED.
   */
  protected int getDoStartValue()
    throws JspException
  {
    return BodyTag.EVAL_BODY_BUFFERED;
  }

  public void doInitBody()
    throws JspException
  {
  }

  public int doAfterBody()
    throws JspException
  {
    UIComponent verbatim = createVerbatimComponentFromBodyContent();

    if (verbatim != null) {
      UIComponent component = getComponentInstance();

      if (component != null)
        component.getChildren().add(verbatim);
    }

    return getDoAfterBodyValue();
  }

  protected int getDoAfterBodyValue()
    throws JspException
  {
    return BodyTag.SKIP_PAGE;
  }

  public int doEndTag()
    throws JspException
  {
    pageContext.getRequest().setAttribute("caucho.jsf.parent",
                                          _parentUIComponentTag);

    return getDoEndValue();
  }

  protected int getDoEndValue()
    throws JspException
  {
    return Tag.EVAL_PAGE;
  }

  protected abstract UIComponent createComponent(FacesContext context,
                                                 String newId)
    throws JspException;

  protected abstract void setProperties(UIComponent component);

  protected UIComponent findComponent(FacesContext context)
    throws JspException
  {
    _created = false;

    if (_component != null)
      return _component;

    UIComponentClassicTagBase parentTag = _parentUIComponentTag;

    if (parentTag == null) {
      _component = context.getViewRoot();

      // XXX:
      if (_component.getChildCount() == 0){
        _created = true;

        setProperties(_component);
      }

      return _component;
    }

    UIComponent verbatim = parentTag.createVerbatimComponentFromBodyContent();

    UIComponent parent = parentTag.getComponentInstance();

    String id = getId();
    String facetName = null;

    if (id == null)
      id = UIViewRoot.UNIQUE_ID_PREFIX + getJspId();

    Map tagMap = (Map) pageContext.getRequest()
      .getAttribute("caucho.jsf.tag.map");

    Integer iterCounter = (Integer) tagMap.get(_jspId);

    if (iterCounter.intValue() > 0)
      id = id + "_" + iterCounter.intValue();

    if (_parent instanceof FacetTag) {
      facetName = ((FacetTag) _parent).getName();

      _component = parent.getFacet(facetName);

      if (_component != null)
        return _component;
    }
    else {
      _component = parent.findComponent(id);

      if (_component != null) {
        if (verbatim != null) {
          addVerbatimBeforeComponent(parentTag, verbatim, _component);
        }

        return _component;
      }

      if (verbatim != null) {
        parent.getChildren().add(verbatim);
      }
    }

    String componentType = getComponentType();

    if (hasBinding()) {
      // XXX: binding
    }

    _component = createComponent(context, id);

    _created = true;

    _component.setId(id);

    setProperties(_component);

    if (facetName != null)
      parent.getFacets().put(facetName, _component);
    else
      parent.getChildren().add(_component);

    return _component;
  }

  protected int getIndexOfNextChildTag()
  {
    throw new UnsupportedOperationException();
  }

  protected void addChild(UIComponent child)
  {
    getComponentInstance().getChildren().add(child);
  }

  protected void addFacet(String name)
  {
    throw new UnsupportedOperationException();
  }

  protected UIComponent createVerbatimComponentFromBodyContent()
  {
    BodyContent bodyContent = this.bodyContent;

    if (bodyContent == null)
      return null;

    String text = bodyContent.getString();
    bodyContent.clearBody();
    boolean isWhitespace = true;

    for (int i = text.length() - 1; i >= 0; i--) {
      char ch = text.charAt(i);

      if (!Character.isWhitespace(ch)) {
        // check for comment
        if (ch == '>' && text.indexOf("-->") + 2 == i) {
          int head = text.indexOf("<!--");

          if (head >= 0) {
            for (int j = 0; j < head; j++) {
              if (!Character.isWhitespace(text.charAt(j))) {
                isWhitespace = false;
                break;
              }
            }

            if (isWhitespace)
              return null;
          }
        }

        isWhitespace = false;
        break;
      }
    }

    if (isWhitespace)
      return null;

    UIOutput verbatim = createVerbatimComponent();

    verbatim.setValue(text);

    return verbatim;
  }

  protected UIOutput createVerbatimComponent()
  {
    Application app = _facesContext.getApplication();
    UIOutput output
      = (UIOutput) app.createComponent(HtmlOutputText.COMPONENT_TYPE);

    output.setId(_facesContext.getViewRoot().createUniqueId());
    output.setTransient(true);
    if (output instanceof HtmlOutputText)
      ((HtmlOutputText) output).setEscape(false);

    return output;
  }

  protected void addVerbatimBeforeComponent(UIComponentClassicTagBase parentTag,
                                            UIComponent verbatim,
                                            UIComponent component)
  {
    UIComponent parent = parentTag.getComponentInstance();

    int size = parent.getChildCount();

    if (size > 0) {
      List<UIComponent> children = parent.getChildren();

      for (int i = 0; i < size; i++) {
        if (children.get(i) == component)
          children.add(i, verbatim);
      }
    }
  }

  protected void addVerbatimAfterComponent(UIComponentClassicTagBase parentTag,
                                           UIComponent verbatim,
                                           UIComponent component)
  {
    UIComponent parent = parentTag.getComponentInstance();

    int size = parent.getChildCount();

    if (size > 0) {
      List<UIComponent> children = parent.getChildren();

      for (int i = 0; i < size; i++) {
        if (children.get(i) == component)
          children.add(i + 1, verbatim);
      }
    }
  }

  protected List<String> getCreatedComponents()
  {
    throw new UnsupportedOperationException();
  }

  protected FacesContext getFacesContext()
  {
    return _facesContext;
  }

  /**
   * @deprecated
   */
  protected void setupResponseWriter()
  {
  }

  /**
   * @deprecated
   */
  protected void encodeBegin()
    throws IOException
  {
    UIComponent component = getComponentInstance();
    FacesContext context = getFacesContext();

    if (component != null && context != null)
      component.encodeBegin(context);
  }

  /**
   * @deprecated
   */
  protected void encodeChildren()
    throws IOException
  {
    UIComponent component = getComponentInstance();
    FacesContext context = getFacesContext();

    if (component != null && context != null)
      component.encodeChildren(context);
  }

  /**
   * @deprecated
   */
  protected void encodeEnd()
    throws IOException
  {
    UIComponent component = getComponentInstance();
    FacesContext context = getFacesContext();

    if (component != null && context != null)
      component.encodeEnd(context);
  }

  public void release()
  {
  }

  public static UIComponentClassicTagBase
  getParentUIComponentClassicTagBase(PageContext pageContext)
  {
    return (UIComponentClassicTagBase)
      pageContext.getRequest().getAttribute("caucho.jsf.parent");
  }
}
