/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/jsf/dev/JsfDeveloperAid.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 Alex Rojkov
 */

package com.caucho.jsf.dev;

import com.caucho.util.L10N;
import com.caucho.jsf.webapp.FacesServletImpl;
import com.caucho.server.webapp.WebApp;

import javax.el.ValueExpression;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UIViewRoot;
import javax.faces.component.ValueHolder;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Array;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class
  JsfDeveloperAid
  implements PhaseListener
{

  public static final String URL_PATTERN = "caucho.jsf.developer.aid";

  private static final Logger log
    = Logger.getLogger(FacesServletImpl.class.getName());

  private static final L10N L = new L10N(JsfDeveloperAid.class);

  private String _developerAidLinkStyle;

  public JsfDeveloperAid()
  {
    WebApp webApp = WebApp.getCurrent();

    _developerAidLinkStyle = webApp.getJsf().getDeveloperAidLinkStyle();
  }

  public void afterPhase(PhaseEvent event)
  {
    final FacesContext context = event.getFacesContext();

    final ExternalContext exContext = context.getExternalContext();

    final Map<String, Object> sessionMap = exContext.getSessionMap();

    Map<String, JsfRequestSnapshot> aidMap
      = (Map<String, JsfRequestSnapshot>) sessionMap.get(
      "caucho.jsf.developer.aid");

    if (aidMap == null) {
      aidMap = new HashMap<String, JsfRequestSnapshot>();

      sessionMap.put("caucho.jsf.developer.aid", aidMap);
    }

    try {
      final UIViewRoot uiViewRoot = context.getViewRoot();

      if (uiViewRoot != null) {
        final String viewId = uiViewRoot.getViewId();
        final String phaseId = event.getPhaseId().toString();

        final ViewRoot viewRoot = (ViewRoot) reflect(context, uiViewRoot);
        viewRoot.setPhase(phaseId);

        //request attributes
        Map<String, Object> requestMap = exContext.getRequestMap();
        Map<String, Bean> requestSnapshot = new HashMap<String, Bean>();

        for (String key : requestMap.keySet()) {
          if (key.startsWith("caucho.") ||
              key.startsWith("com.caucho.") ||
              key.startsWith("javax."))
            continue;

          Bean bean = reflect(requestMap.get(key));

          requestSnapshot.put(key, bean);
        }

        viewRoot.setRequestMap(requestSnapshot);
        

        //session attributes
        Map<String, Bean> sessionSnapshot = new HashMap<String, Bean>();

        for (String key : sessionMap.keySet()) {
          if (key.startsWith("caucho.") ||
              key.startsWith("com.caucho.") ||
              key.startsWith("javax."))
            continue;

          Bean bean = reflect(sessionMap.get(key));

          sessionSnapshot.put(key, bean);
        }

        viewRoot.setSessionMap(sessionSnapshot);

        //application attributes
        Map<String, Object> applicationMap = exContext.getApplicationMap();
        Map<String, Bean> applicationSnapshot = new HashMap<String, Bean>();

        for (String key : applicationMap.keySet()) {
          if (key.startsWith("caucho.") ||
              key.startsWith("com.caucho.") ||
              key.startsWith("javax."))
            continue;

          Bean bean = reflect(applicationMap.get(key));

          applicationSnapshot.put(key, bean);
        }

        viewRoot.setApplicationMap(applicationSnapshot);

        JsfRequestSnapshot snapshot;

        if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId())) {
          snapshot = new JsfRequestSnapshot();

         //headers
          Map<String, String> map = exContext.getRequestHeaderMap();
          snapshot.setHeaderMap(new HashMap<String, String>(map));

          //parameters
          map = exContext.getRequestParameterMap();
          snapshot.setParameterMap(new HashMap<String, String>(map));

          aidMap.put(viewId, snapshot);
        }
        else {
          snapshot = aidMap.get(viewId);
        }

        snapshot.addViewRoot(viewRoot);
      }
    }
    catch (IllegalStateException e) {
      log.log(Level.FINER, e.getMessage(), e);
    }
    catch (Throwable t) {
      log.log(Level.FINER, t.getMessage(), t);
    }
  }

  public void beforePhase(PhaseEvent event)
  {
    if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
      return;

    UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();

    if (viewRoot == null)
      return;

    JsfDeveloperAidLink link = new JsfDeveloperAidLink();

    link.setStyle(_developerAidLinkStyle);

    viewRoot.getChildren().add(link);
  }

  public PhaseId getPhaseId()
  {
    return PhaseId.ANY_PHASE;
  }

  public Component reflect(FacesContext facesContext, UIComponent uiComponent)
  {
    final Component result;

    if (uiComponent instanceof UIViewRoot) {
      UIViewRoot uiViewRoot = (UIViewRoot) uiComponent;
      result = new ViewRoot();

      ViewRoot viewRoot = (ViewRoot) result;

      viewRoot.setLocale(uiViewRoot.getLocale());
      viewRoot.setRenderKitId(uiViewRoot.getRenderKitId());
    }
    else
      result = new Component();

    result._uiComponentClass = uiComponent.getClass().getSimpleName();
    result._clientId = uiComponent.getClientId(facesContext);
    result._family = uiComponent.getFamily();

    final int childCount = uiComponent.getChildCount();

    if (childCount > 0) {
      List<UIComponent> children = uiComponent.getChildren();

      result._children = new ArrayList<Component>(children.size());

      for (int i = 0; i < childCount; i++) {
        UIComponent child = children.get(i);

        if (!(child instanceof JsfDeveloperAidLink))
          result._children.add(reflect(facesContext, children.get(i)));
      }
    }

    final int facetCount = uiComponent.getFacetCount();

    if (facetCount > 0) {
      Map<String, UIComponent> facets = uiComponent.getFacets();

      result._facets = new HashMap<String, Component>(facets.size());

      Set<String> names = facets.keySet();

      for (String name : names) {
        UIComponent child = facets.get(name);

        result._facets.put(name, reflect(facesContext, child));
      }
    }

    if (uiComponent instanceof ValueHolder) {
      result._isValueHolder = true;

      Object value;

      try {
        value = ((ValueHolder) uiComponent).getValue();
      }
      catch (Throwable t) {
        value = "Failed due to: " + t.getMessage();
      }

      result._value = String.valueOf(value);

      Object localValue;

      try {
        localValue = ((ValueHolder) uiComponent).getLocalValue();
      }
      catch (Throwable t) {
        localValue = "Failed due to: " + t.getMessage();
      }

      result._localValue = String.valueOf(localValue);
    }

    if (uiComponent instanceof EditableValueHolder) {
      result._isEditableValueHolder = true;

      Object submittedValue;

      try {
        submittedValue
          = ((EditableValueHolder) uiComponent).getSubmittedValue();
      }
      catch (Throwable t) {
        submittedValue = "Failed due to: " + t.getMessage();
      }

      if (submittedValue instanceof Object[]) {

        StringBuilder sb = new StringBuilder('[');

        Object []values = (Object[]) submittedValue;

        for (int i = 0; i < values.length; i++) {
          Object value = values[i];

          sb.append(String.valueOf(value));

          if ((i + 1) < values.length)
            sb.append(',');
        }

        sb.append(']');

        result._submittedValue = sb.toString();
      }
      else {
        result._submittedValue = String.valueOf(submittedValue);
      }
    }

    for (Method method : uiComponent.getClass().getMethods()) {
      if (!method.getName().startsWith("get")
          && !method.getName().startsWith("is"))
        continue;
      else if (method.getParameterTypes().length != 0)
        continue;

      String name;

      if (method.getName().startsWith("get"))
        name = method.getName().substring(3);
      else if (method.getName().startsWith("is"))
        name = method.getName().substring(2);
      else
        continue;

      name = Character.toLowerCase(name.charAt(0)) + name.substring(1);

      ValueExpression expr = uiComponent.getValueExpression(name);

      Class type = method.getReturnType();

      if (expr != null) {
        result.setAttribute("expr:" + name, expr.getExpressionString());
      }
      else if (method.getDeclaringClass().equals(UIComponent.class)
               || method.getDeclaringClass().equals(UIComponentBase.class)) {
      }
      else if (name.equals("family") ||
               name.equals("value") ||
               name.equals("localValue") ||
               name.equals("submittedValue")) {
      }
      else if (String.class.equals(type)) {
        try {
          Object value = method.invoke(uiComponent);

          if (value != null)
            result.setAttribute(name, String.valueOf(value));
        }
        catch (Exception e) {
        }
      }
    }

    return result;
  }

  public Bean reflect(Object obj)
  {
    if (obj == null)
      return null;

    final Bean result;

    if (obj instanceof String
        || obj instanceof Boolean
        || obj instanceof Character
        || obj instanceof Number
        || obj instanceof Date
      ) {
      result = new Bean();

      result.setClassName(obj.getClass().getSimpleName());
      result.setToString(obj.toString());
      result.setSimple(true);
    } else if (obj instanceof Object[]) {
      result = new Bean();

      result.setArray(true);
      result.setClassName(obj.getClass().getComponentType().getName());
      result.setLength(Array.getLength(obj));
    }
    else {
      result = new Bean();

      result.setClassName(obj.getClass().getName());
      result.setToString(obj.toString());

      Field []fields = obj.getClass().getDeclaredFields();

      Map<String, String> attributes = new HashMap<String, String>();

      for (Field field : fields) {
        try {
          field.setAccessible(true);

          Object value = field.get(obj);

          attributes.put(field.getName(), String.valueOf(value));
        }
        catch (IllegalAccessException e) {
        }
      }

      result.setAttributes(attributes);
    }

    return result;
  }


  public static class JsfRequestSnapshot
    implements Serializable
  {
    private ViewRoot []_phases;
    private Map<String, String> _parameterMap;
    private Map<String, String> _headerMap;

    public void addViewRoot(ViewRoot viewRoot)
    {
      if (_phases == null) {
        _phases = new ViewRoot[]{viewRoot};
      }
      else {
        ViewRoot []newPhases = new ViewRoot[_phases.length + 1];

        System.arraycopy(_phases, 0, newPhases, 0, _phases.length);

        newPhases[newPhases.length - 1] = viewRoot;

        _phases = newPhases;
      }
    }

    public void setPhases(ViewRoot []phases)
    {
      _phases = phases;
    }

    public ViewRoot[] getPhases()
    {
      return _phases;
    }

    public Map<String, String> getParameterMap()
    {
      return _parameterMap;
    }

    public void setParameterMap(Map<String, String> parameterMap)
    {
      _parameterMap = parameterMap;
    }

    public Map<String, String> getHeaderMap()
    {
      return _headerMap;
    }

    public void setHeaderMap(Map<String, String> headerMap)
    {
      _headerMap = headerMap;
    }

  }

  public static class Bean
    implements Serializable
  {
    private Map<String, String> _attributes;
    private String _className;
    private String _toString;
    private boolean _isArray;
    private int _length;
    private boolean _simple;

    public String getToString()
    {
      return _toString;
    }

    public void setToString(String toString)
    {
      _toString = toString;
    }

    public String getClassName()
    {
      return _className;
    }

    public void setClassName(String className)
    {
      _className = className;
    }

    public Map<String, String> getAttributes()
    {
      return _attributes;
    }

    public void setAttributes(Map<String, String> attributes)
    {
      _attributes = attributes;
    }

    public boolean isArray()
    {
      return _isArray;
    }

    public void setArray(boolean array)
    {
      _isArray = array;
    }

    public int getLength()
    {
      return _length;
    }

    public void setLength(int length)
    {
      _length = length;
    }

    public boolean isSimple()
    {
      return _simple;
    }

    public void setSimple(boolean simple)
    {
      _simple = simple;
    }
  }

  public static class ViewRoot
    extends Component
  {
    private Locale _locale;
    private String _renderKitId;
    private String _phase;
    private Map<String, Bean> _requestMap;
    private Map<String, Bean> _sessionMap;
    private Map<String, Bean> _applicationMap;


    public Locale getLocale()
    {
      return _locale;
    }

    public void setLocale(Locale locale)
    {
      _locale = locale;
    }

    public String getRenderKitId()
    {
      return _renderKitId;
    }

    public void setRenderKitId(String renderKitId)
    {
      _renderKitId = renderKitId;
    }

    public String getPhase()
    {
      return _phase;
    }

    public void setPhase(String phase)
    {
      _phase = phase;
    }

    public Map<String, Bean> getRequestMap()
    {
      return _requestMap;
    }

    public void setRequestMap(Map<String, Bean> requestMap)
    {
      _requestMap = requestMap;
    }

    public Map<String, Bean> getSessionMap()
    {
      return _sessionMap;
    }

    public void setSessionMap(Map<String, Bean> sessionMap)
    {
      _sessionMap = sessionMap;
    }

    public Map<String, Bean> getApplicationMap()
    {
      return _applicationMap;
    }

    public void setApplicationMap(Map<String, Bean> applicationMap)
    {
      _applicationMap = applicationMap;
    }

  }

  public static class Component
    implements Serializable
  {
    private String _uiComponentClass;
    private String _clientId;
    private String _family;
    private String _value;
    private String _localValue;
    private String _submittedValue;
    private boolean _isValueHolder;
    private boolean _isEditableValueHolder;

    private List<Component> _children;
    private Map<String, Component> _facets;
    private Map<String, String> _attributes;


    public List<Component> getChildren()
    {
      return _children;
    }

    public void setChildren(List<Component> children)
    {
      _children = children;
    }

    public Map<String, Component> getFacets()
    {
      return _facets;
    }

    public void setFacets(Map<String, Component> facets)
    {
      _facets = facets;
    }

    public String getUiComponentClass()
    {
      return _uiComponentClass;
    }

    public void setUiComponentClass(String uiComponentClass)
    {
      _uiComponentClass = uiComponentClass;
    }

    public String getClientId()
    {
      return _clientId;
    }

    public void setClientId(String clientId)
    {
      _clientId = clientId;
    }

    public String getFamily()
    {
      return _family;
    }

    public void setFamily(String family)
    {
      _family = family;
    }

    public String getValue()
    {
      return _value;
    }

    public void setValue(String value)
    {
      _value = value;
    }

    public String getLocalValue()
    {
      return _localValue;
    }

    public void setLocalValue(String localValue)
    {
      _localValue = localValue;
    }

    public String getSubmittedValue()
    {
      return _submittedValue;
    }

    public void setSubmittedValue(String submittedValue)
    {
      _submittedValue = submittedValue;
    }

    public boolean isValueHolder()
    {
      return _isValueHolder;
    }

    public void setValueHolder(boolean valueHolder)
    {
      _isValueHolder = valueHolder;
    }

    public boolean isEditableValueHolder()
    {
      return _isEditableValueHolder;
    }

    public void setEditableValueHolder(boolean editableValueHolder)
    {
      _isEditableValueHolder = editableValueHolder;
    }

    public void setAttribute(String name, String value)
    {
      if (_attributes == null)
        _attributes = new HashMap<String, String>();

      _attributes.put(name, value);
    }

    public Map<String, String> getAttributes()
    {
      return _attributes;
    }

    public void setAttributes(Map<String, String> attributes)
    {
      _attributes = attributes;
    }
  }
}