package org.apache.cocoon.transformation;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.environment.Context;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Session;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.Marshaller;
import org.xml.sax.AttributeList;
import org.xml.sax.Attributes;
import org.xml.sax.HandlerBase;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Description: Marshals a object from the Sitemap, Session, Request or
* the Conext into a series of SAX events
*
* Configuation: The Castortransformer need to be configured with a
* default mapping. This mapping is used as long as no other mapping
* is spezfied as the element
*
*<pre>
* <map:transformer name="CastorTransformer" src="org.apache.cocoon.transformation.CastorTransformer">
* <mapping>castor/xmapping.xml</mapping>
* </map:transformer>
*</pre>
*
* A sample for the use:
* <pre>
* <root xmlns:castor="http://castor.exolab.org/cocoontransfomer">
* <castor:InsertBean name="invoice"/>
* <castor:InsertBean name="product" scope="sitemap" mapping="castor/specicalmapping.xml"/>
* </root>
* </pre>
* The CastorTransfomer support only one Element <code>castor:InsertBean</code>. This
* element is replaced with the marshalled object. The Object given through the
* attrbute <code>name</code> will be searched in the <code>sitemap, request,
* session</code> and at least in <code>application</code>
* If the scope is explicitly given, e.g , the object will ge located only here
* The Attribut <code>mapping</code> specifys the mapping to be used. If not given
* the default mapping is used
* <pre/>
* Author <a href="mailto:mauch@imkenberg.de">Thorsten Mauch</a>
*
*/
public class CastorTransformer extends AbstractTransformer implements Configurable {
private static String CASTOR_URI="http://castor.exolab.org/cocoontransfomer";
private boolean in_castor_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";
final static String MAPPING_CONFIG ="mapping";
private final static String FILE_PREFIX="file:";
private HandlerBase CastorEventAdapter;
private Map objectModel;
// stores all used mappings in the cache
private static HashMap mappingCache;
private String defaultmapping="castor/mapping.xml";
private SourceResolver resolver;
public CastorTransformer() {
/**
* Inner class eventhandler, forward the Castor SAX events
* to Cocoon 2 Events
*/
CastorEventAdapter = new HandlerBase(){
public void startElement(String name, AttributeList attributes) throws SAXException
{
AttributesImpl a= new AttributesImpl();
for(int i=0;i <attributes.getLength(); i++){
a.addAttribute("",attributes.getName(i),attributes.getName(i),
"",attributes.getValue(i));
}
CastorTransformer.super.contentHandler.startElement("",name,name,a);
}
public void characters(char[] chars, int offset, int length) throws SAXException
{
CastorTransformer.super.contentHandler.characters(chars, offset, length);
}
public void endElement(String name) throws SAXException
{
CastorTransformer.super.contentHandler.endElement("", name,name);
}
};
}
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(CASTOR_URI.equals(uri)){
in_castor_element= false;
return;
}
super.endElement( uri, name, raw);
}
public void startElement(String uri, String name, String raw, Attributes attr) throws org.xml.sax.SAXException {
if(CASTOR_URI.equals(uri)){
in_castor_element= true;
process(name,attr);
return;
}
super.startElement( uri, name, raw, attr);
}
public void characters(char[] ch, int start, int len) throws org.xml.sax.SAXException {
if(in_castor_element)
return;
super.characters(ch,start, len);
}
private void process(String command,Attributes attr){
if(command.equals(CMD_INSERT_BEAN)) {
String sourcemap = attr.getValue(ATTRIB_SCOPE);
String name = attr.getValue(ATTRIB_NAME);
String mapping = attr.getValue("mapping");
Object toInsert;
Request request = ObjectModelHelper.getRequest(objectModel);
if(name == null){
getLogger().error("attribut to insert not set");
}
/*
searcl all maps for the given bean
*/
else{
if( sourcemap == null || VALUE_SITEMAP.equals(sourcemap)){
//System.out.println("Searching bean " + name+ " in "+VALUE_SITEMAP);
toInsert=objectModel.get(name);
if(toInsert != null){
insertBean(toInsert,mapping);
return;
}
}
if( sourcemap == null || VALUE_REQUEST.equals(sourcemap)){
//System.out.println("Searching bean " + name+ " in "+ VALUE_REQUEST);
toInsert=request.getAttribute(name);
if(toInsert != null){
insertBean(toInsert,mapping);
return;
}
}
if(sourcemap == null || VALUE_SESSION.equals(sourcemap)){
//System.out.println("Searching bean " + name+ " in "+VALUE_SESSION);
Session session =request.getSession(false);
if(session != null){
toInsert=session.getAttribute(name);
if(toInsert != null){
insertBean(toInsert,mapping);
return;
}
}
}
if(sourcemap == null || VALUE_CONTEXT.equals(sourcemap)){
Context context = ObjectModelHelper.getContext(objectModel);
if(context != null){
toInsert=context.getAttribute(name);
if(toInsert != null){
insertBean(toInsert,mapping);
return;
}
}
}
}
getLogger().debug("Bean " +name + " could not be found");
return;
} // end CMD_INSERT_BEAN
getLogger().error("Unknown command: " +command);
}
private void insertBean(Object bean,String mappingpath){
if(bean == null){
getLogger().debug ("no bean found");
return;
}
try{
Mapping mapping;
if(mappingpath != null){
mapping=mappingLoader(mappingpath);
}
else{
mapping=mappingLoader(defaultmapping);
}
Marshaller marshaller= new Marshaller(CastorEventAdapter);
marshaller.setMapping(mapping);
marshaller.marshal(bean);
}
catch(Exception e){
e.printStackTrace();
}
}
private Mapping mappingLoader(String path) throws MappingException,IOException{
if(mappingCache == null){
mappingCache= new HashMap();
}
// cache already exsit
else{
// and contain the mapping already
if(mappingCache.containsKey(path)){
return (Mapping)mappingCache.get(path);
}
}
// mapping not found in cache or the cache is new
Mapping mapping = new Mapping();
mapping.loadMapping(getFile(resolver,path));
mappingCache.put(path,mapping);
return mapping;
}
public String getFile(SourceResolver sr, String FileName) {
try{
String path = sr.resolve(FileName).getSystemId();
if(path.startsWith(FILE_PREFIX)){
path = path.substring(FILE_PREFIX.length());
}
return path;
}
catch(Exception e){
getLogger().error("could not read mapping file",e);
return null;
}
}
public void configure(Configuration conf) throws org.apache.avalon.framework.configuration.ConfigurationException {
try{
defaultmapping=conf.getChild(MAPPING_CONFIG).getValue();
}
catch(Exception e){
e.printStackTrace();
getLogger().error("can't load default mapping",e);
}
}
}