package org.apache.cocoon.transformation;

import org.apache.cocoon.environment.SourceResolver;
import java.util.Map;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.xml.*;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.Constants;
import org.apache.cocoon.environment.Session;


import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.SAXException;
import java.util.HashMap;
import java.io.File;
import java.io.IOException;
import org.apache.cocoon.environment.Context;

import de.ispsoft.jaxme.JMAnyElement;


/**
 * Description: Marshals a object from the Sitemap, Session, Request or
 * the Context into a series of SAX events
 *
 * Configuation: The JaxMeTransformer need to be configured with a
 * default mapping. This mapping is used as long as no other mapping
 * is specified as the element
 *
 *<pre>
 *  &lt;map:transformer name="JaxMeTransformer" src="org.apache.cocoon.transformation.JaxMeTransformer"/&gt;
 *</pre>
 *
 * A sample for the use:
 * <pre>
 *   &lt;root xmlns:jaxme="http://ispsoft.de/namespaces/jaxme/cocoon/jaxmetransfomer"&gt;
 *	    &lt;jaxme:InsertBean name="invoice"/&gt;
 *	    &lt;jaxme:InsertBean name="product" scope="sitemap"/&gt;
 *  &lt/root&gt
 * </pre>
 * The JaxMeTransfomer supports only one Element <code>jaxme:InsertBean</code>. This
 * element is replaced with the marshalled object. The Object given through the
 * attribute <code>name</code> will be searched in the <code>sitemap</code>,
 * <code>request</code>, <code>session</code> and at least in <code>application</code>.
 * If the scope is explicitly given, e.g , the object will be located only here.
 * <pre/>
 * 
 * @author <a href="mailto:joe@ispsoft.de">Jochen Wiedmann</a>
 */

public class JaxMeTransformer extends AbstractTransformer {
  private static String JAXME_URI = "http://ispsoft.de/namespaces/jaxme/cocoon/jaxmetransfomer";
  private boolean in_jaxme_element = false;
  final static String CMD_INSERT_BEAN="InsertBean";
  final static String ATTRIB_NAME=  "name";
  final static String ATTRIB_SCOPE=  "scope";
  final static String VALUE_SITEMAP ="sitemap";
  final static String VALUE_SESSION ="session";
  final static String VALUE_REQUEST ="request";
  final static String VALUE_CONTEXT ="context";

  private Map objectModel;
  private SourceResolver resolver;

  public JaxMeTransformer() {}


  public void setup(SourceResolver resolver, Map objectModel, String src,
                     Parameters params)
    throws org.apache.cocoon.ProcessingException, org.xml.sax.SAXException,
        java.io.IOException {
    this.objectModel=objectModel;
    this.resolver=resolver;
  }

  public void endElement(String uri, String name, String raw)
      throws org.xml.sax.SAXException {
    if (JAXME_URI.equals(uri)) {
      in_jaxme_element = false;
      return;
    }
    super.endElement( uri,  name,  raw);
  }

  public void startElement(String uri, String name, String raw, Attributes attr)
      throws SAXException {
    if (JAXME_URI.equals(uri)){
        process(name,attr);
        in_jaxme_element= true;
        return;
    }
    super.startElement( uri,  name,  raw,  attr);
  }

  public void characters(char[] ch, int start, int len) throws org.xml.sax.SAXException {
      if(in_jaxme_element)
        return;
      super.characters(ch, start, len);
  }

  private void process(String command, Attributes attr) throws SAXException {
    if (command.equals(CMD_INSERT_BEAN)) {
      String sourcemap = attr.getValue(ATTRIB_SCOPE);
      String name = attr.getValue(ATTRIB_NAME);

      Request request =(Request)objectModel.get(Constants.REQUEST_OBJECT);

      if (name == null) {
        getLogger().error("attribut to insert not set");
      } else {
        /* search all maps for the given bean
         */
        if (sourcemap == null || VALUE_SITEMAP.equals(sourcemap)) {
          Object toInsert = objectModel.get(name);
          if (toInsert != null) {
            insertBean(toInsert);
            return;
          }
        }

        if (sourcemap == null || VALUE_REQUEST.equals(sourcemap)) {
          Object toInsert = request.getAttribute(name);
          if (toInsert != null) {
            insertBean(toInsert);
            return;
          }
        }

        if (sourcemap == null || VALUE_SESSION.equals(sourcemap)) {
          Session session = request.getSession(false);
          if (session != null) {
            Object toInsert = session.getAttribute(name);
            if (toInsert != null) {
              insertBean(toInsert);
              return;
            }
          }
        }

        if (sourcemap == null || VALUE_CONTEXT.equals(sourcemap)) {
          Context context = (Context) objectModel.get(Constants.CONTEXT_OBJECT);
          if (context != null) {
            Object toInsert = context.getAttribute(name);
            if (toInsert != null) {
              insertBean(toInsert);
              return;
            }
          }
        }
      }
      getLogger().debug("Bean " +name + " could not be found");
    } else {
      getLogger().error("Unknown command: " +command);
    }
  }

  private void insertBean(Object bean) throws SAXException {
    if (bean == null) {
      getLogger().debug ("no bean found");
      return;
    }

    JMAnyElement element;
    try {
      element = (JMAnyElement) bean;
    } catch (ClassCastException e) {
      getLogger().error("Supplied bean is not a JMAnyElement");
      return;
    }
    element.toSAX(this);
  }
}
