[Yanel-commits] rev 47362 - in public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources: . jellyadapterofcmdv3

michi at wyona.com michi at wyona.com
Sat Jan 30 15:57:02 CET 2010


Author: michi
Date: 2010-01-30 15:57:02 +0100 (Sat, 30 Jan 2010)
New Revision: 47362

Added:
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/BasicFormResource.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/Continuation.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ControllerAdapter.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ConversationState.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyAdapterForCUDResource.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyControllerAdapter.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyConversationAdapter.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ResourceAdapter.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ViewDescriptorUsingTemplate.java
Removed:
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java
   public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java
Log:
classes moved into unique package name

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,324 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map.Entry;
-
-import org.apache.log4j.Logger;
-import org.wyona.yanel.core.ResourceNotFoundException;
-import org.wyona.yanel.core.attributes.viewable.View;
-
-import javax.mail.Message;
-import javax.mail.Session;
-import javax.mail.Transport;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
-
-/**
- * Base class to handle forms. Returns the following XML streams:
- * 
- * <p>Initial call:</p>
- * <pre>
- * &lt;form-resource&gt;
- *   &lt;form/&gt;
- * &lt;/form-resource&gt;
- * </pre>
- * 
- * <p>Validation failed:</p>
- * <pre>
- * &lt;form-resource&gt;
- *   &lt;form/&gt;
- *   &lt;validation/&gt;
- *   &lt;input/&gt;
- *   &lt;result/&gt;
- * &lt;/form-resource&gt;
- * </pre>
- * 
- * <p>Successful processing:</p>
- * <pre>
- * &lt;form-resource&gt;
- *   &lt;confirmation/&gt;
- *   &lt;input/&gt;
- *   &lt;result/&gt;
- * &lt;/form-resource&gt;
- * </pre>
- * 
- * <p>Error occurred:</p>
- * <pre>
- * &lt;form-resource&gt;
- *   &lt;error/&gt;
- *   &lt;input/&gt;
- *   &lt;result/&gt;
- * &lt;/form-resource&gt;
- * </pre>
- * 
- * @author Matthias Leumann
- * 
- */
-public abstract class BasicFormResource extends BasicXMLResource {
-    
-    private static final Logger log = Logger.getLogger(BasicFormResource.class);
-    
-    private static final String ENCODING = "UTF-8";
-    private static final String INPUT_SUBMIT = "submit";
-    
-    protected static final String RESULT_CODE_DONE_SUCCESSFUL = "done-successful";
-    private static final String RESULT_CODE_FAILED_INVALID = "failed-invalid";
-    private static final String RESULT_CODE_FAILED_ERROR = "failed-error";
-    
-    private String resultCode;
-    private StringBuilder resultXml = new StringBuilder();
-    private LinkedList<NameValuePair> validationErrors = new LinkedList<NameValuePair>();
-
-    public View getView(String viewId) throws Exception {
-        String sslRedirectParam = getResourceConfigProperty("ssl-redirect");
-        if (sslRedirectParam != null && sslRedirectParam.equals("true")) {
-            if (!getEnvironment().getRequest().isSecure() && getEnvironment().getRequest().getParameter("ssl") == null) {
-                View view = new View();
-                view.setResponse(false);
-                URL sslURL;
-                int sslPort = 443;
-                if (getRealm().isProxySet()) {
-                    if (realm.getProxySSLPort() >= 0) sslPort = realm.getProxySSLPort();
-                    String requestURI = getEnvironment().getRequest().getRequestURI();
-                    if (realm.getProxyPrefix() != null) requestURI = requestURI.substring(realm.getProxyPrefix().length());
-                    sslURL = new URL("https", getRealm().getProxyHostName(), sslPort, requestURI + "?ssl=true");
-                } else {
-                    sslURL = new URL("https", getEnvironment().getRequest().getServerName(), sslPort, getEnvironment().getRequest().getRequestURI() + "?ssl=true");
-                }
-                log.warn("Redirect to SSL: " + sslURL.toString());
-                getEnvironment().getResponse().setHeader("Location", sslURL.toString());
-                getEnvironment().getResponse().setStatus(javax.servlet.http.HttpServletResponse.SC_TEMPORARY_REDIRECT);
-                return view;
-            }
-        }
-        return super.getView(viewId);
-    }
-    
-    protected final InputStream getContentXML(String viewId) throws Exception {
-        if (!exists()) {
-            log.warn("No such resource: " + getPath());
-            throw new ResourceNotFoundException("No such resource: " + getPath());
-        }
-        
-        resultXml.append("<form-resource xmlns=\""+getNamespaceURI()+"\">");
-        if (isFormSubmitted()) {
-            validate();
-            if (isInputValid()) {
-                try {
-                    resultCode = execute();
-                    appendConfirmation();
-                } catch (Exception e) {
-                    log.error(e, e);
-                    resultCode = RESULT_CODE_FAILED_ERROR;
-                    appendError(e);
-                }
-            } else {
-                resultCode = RESULT_CODE_FAILED_INVALID;
-                appendForm();
-                appendValidation();
-            }
-            appendInput();
-            appendResult();
-        } else {
-           appendForm();
-        }
-        resultXml.append("</form-resource>");
-        return new ByteArrayInputStream(resultXml.toString().getBytes(ENCODING));
-    }
-    
-    /**
-     * Adds a validation error. Use this method to report invalid input.
-     * 
-     * @param inputName name of the input parameter 
-     * @param validationMessage validation message (may use i18n key)
-     */
-    protected final void addValidationError(String inputName, String validationMessage) {
-        validationErrors.add(new NameValuePair(inputName, validationMessage));
-    }
-    
-    /**
-     * @deprecated Use org.wyona.yanel.core.util.MailUtil.send() instead
-     *
-     * Sends an email. E.g. use this method to send confirmation mails.
-     * 
-     * @param from sender email address
-     * @param replyTo email address (if null, then no reply-to will be set)
-     * @param to receiver email address
-     * @param subject email subject
-     * @param content email content
-     */
-    protected void sendMail(String from, String replyTo, String to, String subject, String content) throws Exception {
-        log.warn("DEPRECATED");
-        org.wyona.yanel.core.util.MailUtil.send(from, replyTo, to, subject, content);
-
-
-/*
-        // Create a mail session
-        java.util.Properties props = new java.util.Properties();
-        props.put("mail.smtp.host", getResourceConfigProperty("smtpHost"));
-        props.put("mail.smtp.port", "" + getResourceConfigProperty("smtpPort"));
-        // TODO: http://java.sun.com/products/javamail/javadocs/javax/mail/Session.html
-        Session session = Session.getDefaultInstance(props, null);
-
-        // Construct the message
-        Message msg = new MimeMessage(session);
-        msg.setFrom(new InternetAddress(from));
-        if (replyTo != null) {
-            InternetAddress[] replyToAddresses = new InternetAddress[1];
-            replyToAddresses[0] = new InternetAddress(replyTo);
-            msg.setReplyTo(replyToAddresses);
-        }
-        if( log.isDebugEnabled() )
-            log.debug("From: " + msg.getFrom() + ", " + from);
-        msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
-        msg.setSubject(subject);
-        msg.setText(content);
-*/
-
-/*
-        msg.setSubject(subject,"utf-8");
-        msg.setContent(content, "text/plain;charset=utf-8");
-*/
-
-/*
-        // Send the message
-        Transport.send(msg);
-*/
-    }
-    
-    /**
-     * Generates valid XML containing the data to display the form screen.
-     * 
-     * @return valid XML string
-     */
-    protected abstract String generateForm() throws Exception;
-    
-    /**
-     * Generates valid XML containing the data to display the confirmation screen.
-     * 
-     * @return valid XML string
-     */
-    protected abstract String generateConfirmation() throws Exception;
-    
-    /**
-     * Validates the form input. Use the method <code>addValidationError</code> to report invalid input.
-     */
-    protected abstract void validate() throws Exception;
-    
-    /**
-     * Executes custom operation if the form input was successfully validated.
-     * 
-     * @return result code (e.g. "done-successful")
-     * @throws Exception if processing failed.
-     */
-    protected abstract String execute() throws Exception;
-    
-    /**
-     * @return the name space URI of of the resulting XML.
-     */
-    protected abstract String getNamespaceURI();
-    
-    
-    /**
-     * @return returns true if and only if the input parameters contain a parameter "submit".
-     */
-    private boolean isFormSubmitted() {
-        return getParameterAsString(INPUT_SUBMIT) != null;
-    }
-    
-    /**
-     * @return true if and only if there are no validation errors.
-     */
-    private boolean isInputValid() {
-        return validationErrors.isEmpty();
-    }
-    
-    /**
-     * Appends the result code to the result XML.
-     */
-    private void appendResult() {
-        resultXml.append("<result>");
-        resultXml.append(resultCode);
-        resultXml.append("</result>");
-    }
-    
-    /**
-     * Appends the data for the form screen to the result XML.
-     */
-    private void appendForm() throws Exception {
-        resultXml.append("<form>");
-        String form = generateForm();
-        if (form != null) {
-            resultXml.append(form);
-        }
-        resultXml.append("</form>");
-    }
-    
-    /**
-     * Appends the data for the confirmation screen to the result XML.
-     */
-    private void appendConfirmation() throws Exception {
-        resultXml.append("<confirmation>");
-        String confirmation = generateConfirmation();
-        if (confirmation != null) {
-            resultXml.append(confirmation);
-        }
-        resultXml.append("</confirmation>");
-    }
-    
-    /**
-     * Appends the validation messages to the result XML.
-     */
-    private void appendValidation() {
-        resultXml.append("<validation>");
-        for (NameValuePair error : validationErrors) {
-            resultXml.append("<"+error.getName()+">"+error.getValue()+"</"+error.getName()+">");
-        }
-        resultXml.append("</validation>");
-    }
-    
-    /**
-     * Appends the form input to the result XML.
-     */
-    private void appendInput() {
-        resultXml.append("<input>");
-        Iterator it = getParameters().entrySet().iterator();
-        while (it.hasNext()) {
-            Entry param = (Entry)it.next();
-            resultXml.append("<"+param.getKey()+"><![CDATA["+param.getValue()+"]]></"+param.getKey()+">");
-        }
-        resultXml.append("</input>");
-    }
-    
-    private void appendError(Exception e) {
-        resultXml.append("<error>");
-        resultXml.append("<![CDATA[" + e.toString() + "]]>");
-        resultXml.append("</error>");
-    }
-    
-    /**
-     * Private utility class to add validation errors to the list.
-     */
-    private class NameValuePair {
-        private String name;
-        private String value;
-        
-        public NameValuePair(String name, String value) {
-            this.name = name;
-            this.value = value;
-        }
-        
-        public String getName() {
-            return name;
-        }
-        
-        public String getValue() {
-            return value;
-        }
-    }
-
-}

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,31 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-/**
- *
- */
-public class Continuation {
-
-    private String id;
-
-    /**
-     *
-     */
-    public Continuation() {
-        // TODO: Use UUID
-        id = "" + new java.util.Date().getTime();
-    }
-
-    /**
-     *
-     */
-    public Continuation(String id) {
-        this.id = id;
-    }
-
-    /**
-     *
-     */
-    public String getId() {
-        return id;
-    }
-}

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,156 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.apache.log4j.Logger;
-import org.wyona.yanel.core.Constants;
-import org.wyona.yanel.core.Resource;
-import org.wyona.yanel.core.api.attributes.ViewableV2;
-import org.wyona.yanel.core.attributes.viewable.ViewDescriptor;
-
-abstract class ControllerAdapter extends Resource implements ResourceAdapter, ViewableV2 {
-    private static Logger log = Logger.getLogger(ControllerAdapter.class);
-    public static String YANEL_CONTINUATION_ID = "yanel.continuation.id";
-    
-    /**
-     * Resource config property for internationalization
-     * */
-    //TODO: what is that???
-    private static final String I18N_CATALOG_PROPERTY = "i18n-catalogue";
-
-    protected final Set<String> supportedViewIds = new HashSet<String>();
-    
-    private String adaptedResourcePath = null;
-    
-    private Usecase usecase = null;
-    
-    /**
-     * Constants.Request.DEFAULT_VIEW_ID is registered
-     */
-    public ControllerAdapter() {
-        supportedViewIds.add(Constants.Request.DEFAULT_VIEW_ID);
-    }
-    
-    /**
-     * Checks if the path was set, otherwise
-     * looks into available parameters and returns the value of
-     * PARAM_ADAPTED_RESOURCE_PATH
-     * @return null when the parameter is not found
-     * */
-    public final String getAdaptedResourcePath() {
-        if(adaptedResourcePath == null){
-            adaptedResourcePath = getParameterAsString(PARAM_ADAPTED_RESOURCE_PATH); 
-        }
-        return adaptedResourcePath;
-    }
-    
-    public void setAdaptedResourcePath(String adaptedResourcePath) {
-        this.adaptedResourcePath = adaptedResourcePath;
-    }
-    
-    /**
-     * Checks if the usecase was set, otherwise looks into available parameters
-     * @return null when usecase can't be determined
-     * */
-    public final Usecase getUsecase() {
-        if(usecase == null){
-            String u = getParameterAsString(Constants.Request.YANEL_RESOURCE_USECASE);
-            if (u == null) {
-                try {
-                    u = getResourceConfigProperty("usecase");
-                } catch (Exception e) {
-                    log.error(e, e);
-                }
-                if (u == null) {
-                    log.error("No usecase (neither 'create' nor 'update/modify' nor 'remove/delete') has been specified (neither within conversation nor query string nor resource config)!");
-                    return null;
-                }
-            }
-            usecase = Usecase.caseInsensitiveValueOf(u);
-        }
-        return usecase;
-    }
-    
-    public void setUsecase(Usecase usecase) {
-        this.usecase = usecase;
-    }
-    
-    /**
-     * Get view descriptor
-     * @param viewId For example 'video'
-     */
-    public ViewDescriptor getViewDescriptor(String viewId) {
-        ViewDescriptor[] viewDescriptors = getViewDescriptors();
-        for (int i = 0; i < viewDescriptors.length; i++) {
-            if (viewDescriptors[i].getId().equalsIgnoreCase(viewId)) {
-                return viewDescriptors[i];
-            }
-        }
-        return null;
-    }
-    
-    /**
-     * Checks for validity of the viewId. For instance,
-     * "CANCEL" is the same as "cancel". So this method normalizes
-     * the view id.
-     * <p>
-     * When the client is doing some logic for some view, it first should normalize it with this method
-     * 
-     * @return the normalized viewId (lowercase), when the viewId is <code>null</code> it returns the default view id
-     * */
-    protected final String normalize(String viewId) {
-        if(viewId == null || "".equals(viewId.trim())){
-            return Constants.Request.DEFAULT_VIEW_ID;
-        }
-        for (Iterator i = supportedViewIds.iterator(); i.hasNext();) {
-            String id = (String) i.next();
-            if(id.equalsIgnoreCase(viewId)){
-                return id;
-            }
-        }
-        return viewId.toLowerCase();
-    }
-
-    /**
-     * Gets the names of the i18n message catalogues used for the i18n transformation.
-     * Looks for an rc config property named 'i18n-catalogue'. Defaults to 'global'.
-     * @return i18n catalogue name
-     */
-    protected String[] getI18NCatalogueNames() throws Exception {
-        String[] catalogueNames = getResourceConfigProperties(I18N_CATALOG_PROPERTY);
-        if (catalogueNames == null || catalogueNames.length == 0) {
-            catalogueNames = new String[1];
-            catalogueNames[0] = "global";
-        }
-        return catalogueNames;
-    }
-    
-    public long getSize() throws Exception {
-        return -1;
-    }
-    
-    public boolean exists() throws Exception {
-        return true;
-    }
-
-    /**
-     * Returns the current continuation associated with the request which is associated with this resource, or if the request does not have a continuation, creates one.
-     */
-    public Continuation getContinuation() {
-        if (getRequest().getParameter(YANEL_CONTINUATION_ID) != null) {
-            String continuationId = getRequest().getParameter(YANEL_CONTINUATION_ID);
-            log.warn("Return existing continuation: " + continuationId);
-            return new Continuation(continuationId);
-            //return new ContinuationSessionImpl(continuationId, getSession(true));
-            //return new ContinuationYarepRepoImpl(continuationId, continuationRepo);
-        } else {
-            Continuation continuation = new Continuation();
-            //Continuation continuation = new ContinuationSessionImpl(getSession(true));
-            //Continuation continuation = new ContinuationYarepRepoImpl(continuationRepo);
-            log.warn("New continuation created: " + continuation.getId());
-            return continuation;
-        }
-    }
-}

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,108 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.io.Serializable;
-
-import org.wyona.yanel.impl.resources.ResourceAdapter.Usecase;
-
-/**
- * Conversation state is stored in the session.
- * During the conversation with the user the model is modified.
- * However, the model is transient.
- */
-class ConversationState implements Serializable{
-	private static final long serialVersionUID = 1L;
-
-	//private static final long serialVersionUID = 1724827714145174641L;
-    private Usecase usecase;
-
-    /**
-     * The path to the adapted resource, e.g. Creatable, Modifiable
-     */
-    private String resourcePath;
-    private String refererUrl;
-    private String gotoUrl;
-    private String currentViewId;
-    private String previousViewId;
-    private transient Object model;
-    private boolean invalidated;
-    
-    public ConversationState(Object model){
-        this.model = model;
-    }
-    
-    public ConversationState(String currentViewId, Object model){
-        this.currentViewId = currentViewId;
-        this.model = model;
-    }
-    
-    public String getCurrentScreen() {
-        return currentViewId;
-    }
-
-    public String getPreviousScreen() {
-        return previousViewId;
-    }
-    
-    public Object getModel() {
-        return model;
-    }
-    
-    public String getRefererUrl() {
-        return refererUrl;
-    }
-
-    void setRefererUrl(String refererUrl) {
-        this.refererUrl = refererUrl;
-        if(gotoUrl == null){
-            this.gotoUrl = this.refererUrl;
-        }
-    }
-
-    public String getGotoUrl() {
-        return gotoUrl;
-    }
-    
-    public void setGotoUrl(String gotoUrl) {
-        this.gotoUrl = gotoUrl;
-    }
-    
-    /**
-     * Sets the previous and current view id
-     * */
-    void setCurrentScreen(String currentViewId) {
-        this.previousViewId = this.currentViewId;
-        this.currentViewId = currentViewId;
-    }
-
-    void setModel(Object model) {
-        this.model = model;
-    }
-    
-    void invalidate(){
-        invalidated = true;
-    }
-    
-    /**
-     * Invalidated conversation state means that the state is removed from 
-     * the session
-     * */
-    public boolean isInvalidated(){
-        return invalidated;
-    }
-
-    public Usecase getUsecase() {
-        return usecase;
-    }
-
-    public void setUsecase(Usecase usecase) {
-        this.usecase = usecase;
-    }
-
-    public String getResourcePath() {
-        return resourcePath;
-    }
-
-    public void setResourcePath(String resourcePath) {
-        this.resourcePath = resourcePath;
-    }
-}

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,451 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.transform.Transformer;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.jelly.JellyContext;
-import org.wyona.yanel.core.Resource;
-import org.wyona.yanel.core.api.attributes.CreatableV3;
-import org.wyona.yanel.core.api.attributes.DeletableV1;
-import org.wyona.yanel.core.api.attributes.ModifiableV3;
-import org.wyona.yanel.core.api.attributes.creatable.ResourceInput;
-import org.wyona.yanel.core.api.attributes.creatable.ResourceInputItem;
-import org.wyona.yanel.core.api.attributes.creatable.ResourceInputItemCollection;
-import org.wyona.yanel.core.attributes.viewable.View;
-import org.wyona.yanel.core.util.ResourceAttributeHelper;
-import org.wyona.yanel.impl.jelly.FileItem;
-import org.wyona.yanel.servlet.communication.HttpRequest;
-
-import org.apache.log4j.Logger;
-
-/**
- * Adapts a CreatableV3.
- * <p>
- * The templates in addition get the following parameters:
- * <ul>
- * <li>resourceInput - description of the inputs that creatable resource needs
- * <li>url.before.conversation - the VIEW can redirect here when the conversation is cancelled
- * <li>resource.input.collection.action - Decides on what kind of action is taken on collection items. Can be 'add' (default) or 'remove'. 
- * </ul>
- * 
- * This is quite a generic class. Subclasses may want to pass other parameters to the templates.
- * */
-public class JellyAdapterForCUDResource extends JellyConversationAdapter {
-
-    private static Logger log = Logger.getLogger(JellyAdapterForCUDResource.class);
-
-    // Collection items are special. Values can be removed or added/set to specific index
-    public static final String REMOVE_ACTION = "remove";
-    public static final String PARAM_RESOURCE_INPUT_COLLECTION_ACTION = "resource.input.collection.action";
-    public static final String PARAM_RESOURCE_INPUT_COLLECTION_INDEX = "resource.input.collection.index";
-    
-    // The three parameters must be specified together!
-    public static final String PARAM_RESOURCE_INPUT_COLLECTION_OLD_INDEX = "resource.input.collection.old.index";
-    public static final String PARAM_RESOURCE_INPUT_COLLECTION_NEW_INDEX = "resource.input.collection.new.index";
-    public static final String PARAM_TARGET_RESOURCE_INPUT = "target.resource.input";
-    
-    public JellyAdapterForCUDResource() {
-    }
-
-    /**
-     *
-     */
-    public View getView(String viewId)throws Exception{
-        try{
-            ViewDescriptorUsingTemplate viewDescriptor = (ViewDescriptorUsingTemplate)getViewDescriptor(normalize(viewId));
-            if(viewDescriptor == null){
-                throw new IllegalArgumentException("The view descriptor was not found for the given id: '" + viewId + "'");
-            }
-            log.debug("View ID: " + viewDescriptor.getId());
-            
-            if (CANCEL_VIEW_ID.equals(viewDescriptor.getId())) {
-                log.warn("Cancel ...");
-                doCancel();
-                destroyConversation();
-                return super.getView(CANCEL_VIEW_ID);
-            }
-            
-            // Deal with the input
-            init();
-            ConversationState cs = getConversationState();
-            
-            boolean incomingInputIsValid = true;
-            if (cs == null) {
-                throw new IllegalStateException("Conversation state is not available");
-            }
-			String oldIndex = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_OLD_INDEX);
-			String newIndex = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_NEW_INDEX);
-			String [] targetInputs = getParameterAsStringValues(PARAM_TARGET_RESOURCE_INPUT);
-			
-			if(oldIndex != null && newIndex != null){
-			    try {
-			        int o = Integer.parseInt(oldIndex);
-			        int n = Integer.parseInt(newIndex);
-			        
-			        ResourceInput ri = (ResourceInput)cs.getModel();
-			        
-			        // Moving input values
-			        for (String target : targetInputs) {
-			            ResourceInputItem item = ri.getItem(target);
-			            if (item instanceof ResourceInputItemCollection) {
-			                ResourceInputItemCollection itemAsCollection = (ResourceInputItemCollection) item;
-			                itemAsCollection.moveValue(o, n);
-			            }
-			        }
-			    } catch (NumberFormatException e) {
-			        log.warn("oldIndex="+oldIndex+", newIndex="+newIndex+": Illegal indexes passed. Operation skiped");
-			    }
-			}else{
-			    // Fill incoming values and validate. This will also 
-			    incomingInputIsValid = fillIncomingInput(cs);
-			}
-            
-            String nextViewId = viewDescriptor.getId();
-            
-            
-            if(!incomingInputIsValid){
-                if(!viewDescriptor.isFragment()){
-                    // When the screen was not valid go to the previous screen
-                    nextViewId = cs.getPreviousScreen();
-                }else{
-                }
-                // Keep the same screen for the fragment
-                cs.setCurrentScreen(cs.getPreviousScreen());
-            }else {
-                if(!viewDescriptor.isFragment()){
-                    // Now the current screen is the next view
-                    String previous = cs.getPreviousScreen();
-                    cs.setCurrentScreen(nextViewId);
-                    if (DONE_VIEW_ID.equals(nextViewId)) {
-                        // NOTE: assumes that DONE_VIEW_ID is not a fragment
-                        try {
-                            commit();
-                            destroyConversation();
-                        } catch (IllegalArgumentException e) {
-                            log.warn("Cannot commit", e);
-                            //thrown when the ResourceInput is not valid, then go back
-                            nextViewId = previous; 
-                            cs.setCurrentScreen(previous);
-                        }
-                    }
-                }else{
-                    // Keep the previous screen when the input is a fragment
-                    cs.setCurrentScreen(cs.getPreviousScreen());
-                }
-            }
-            return super.getView(nextViewId);
-        } catch(Throwable e){
-            // Cancel the conversation when something goes wrong
-            destroyConversation();
-            
-            throw new Exception("Due to an exception the request has been canceled. Exception message: " + e.getMessage(), e);
-        }
-    }
-
-    /**
-     * Try to set the resource input values according to what came in HTTP
-     * request.
-     * <p>
-     * NOTE[very important]: this method relies on the fact that HTTP request 
-     * has parameters even for checkboxes and select(multiple), which are normally not submitted
-     * when nothing is selected or checked. The "nothing is selected" means that the 
-     * parameter is passed but the value is <code>null</code>. 
-     * This must be ensured in order to use the adapter correctly, e.g. the simple tag library is 
-     * rendering an additional HIDDEN field for this purpose.
-     * @return true when all the incoming input items are valid
-     */
-    private boolean fillIncomingInput(ConversationState cs) throws Exception {
-        ResourceInput ri = (ResourceInput) cs.getModel();
-        String[] itemNames = ri.getItemNames();
-        
-        Set incomingParameterNames = new HashSet(getParameters().keySet());
-        // Add incoming names of the inputs that are files
-        if (getEnvironment().getRequest() instanceof HttpRequest) {
-            HttpRequest httpRequest = (HttpRequest) request;
-            if (httpRequest.isMultipartRequest()) {
-                Enumeration params = httpRequest.getFileNames();
-                while (params.hasMoreElements()) {
-                    incomingParameterNames.add(params.nextElement());
-                }
-            }
-        }
-        //Now we have all incoming parameter names
-        boolean inputValid = true;
-        for (int i = 0; i < itemNames.length; i++) {
-            String action = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_ACTION);
-            String index = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_INDEX);
-            
-            if(!incomingParameterNames.contains(itemNames[i])){
-                // We need to fill only the parameters that were passed
-                continue;
-            }
-            
-            // Go further to addValue()/removeValue()/setValue()
-            
-            Object paramValue = getParameterAsFileItem(itemNames[i]);
-            if(paramValue == null){
-                paramValue = getParameter(itemNames[i]);
-            }
-            
-            if (REMOVE_ACTION.equals(action)){
-                if (ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_COLLECTION) {
-                    ResourceInputItemCollection collectionItem = (ResourceInputItemCollection) ri.getItem(itemNames[i]);
-                    
-                    // Remove the value (maybe at the specified index)
-                    boolean removed = false;
-                    if(paramValue != null){
-                        removed = collectionItem.removeValue(paramValue);
-                    }
-                    if(!removed && index != null){
-                        try{
-                            int valueIndex = Integer.parseInt(index);
-                            collectionItem.removeValue(valueIndex);
-                        }catch(NumberFormatException e){
-                            log.warn(itemNames[i]+"="+paramValue+": Could not remove the parameter at the index "+index);
-                        }
-                    }
-                }else{
-                    ri.getItem(itemNames[i]).setValue(null);
-                }
-            }else{
-                if(ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_FILE_UPLOAD){
-                    FileItem fi = (FileItem)paramValue;
-                    if(fi == null || !fi.hasData()){
-                        // This will cause the old data (maybe null) to be left and validated
-                        inputValid = ri.getItem(itemNames[i]).validate() && inputValid;
-                        continue;
-                    }
-                }
-                
-                if (ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_COLLECTION) {
-                    ResourceInputItemCollection collectionItem = (ResourceInputItemCollection) ri.getItem(itemNames[i]);
-                    if(index != null){
-                        try{
-                            int valueIndex = Integer.parseInt(index);
-                            collectionItem.addValue(valueIndex, paramValue);
-                        }catch(NumberFormatException e){
-                            log.warn(itemNames[i]+"="+paramValue+": Could not add the parameter at the index "+index+", appending");
-                            collectionItem.addValue(paramValue);
-                        }
-                    }else{
-                        collectionItem.addValue(paramValue);
-                    }
-                } else {
-                    ri.getItem(itemNames[i]).setValue(paramValue);
-                }
-            }
-            inputValid = ri.getItem(itemNames[i]).validate() && inputValid;
-        }
-        
-        return inputValid;
-    }
-    
-    /**
-     * @return <code>null</code> when the parameter is not a file upload, otherwise a constructed file item.
-     * */
-    private FileItem getParameterAsFileItem(String parameterName)throws Exception{
-        FileItem fileItem = null;
-        if (getEnvironment().getRequest() instanceof HttpRequest) {
-            HttpRequest httpRequest = (HttpRequest) request;
-            if (httpRequest.isMultipartRequest()) {
-                Enumeration fileParameterNames = httpRequest.getFileNames();
-                while (fileParameterNames.hasMoreElements()) {
-                    // create new file item
-                    String name = (String) fileParameterNames.nextElement();
-                    if (name.equals(parameterName)) {
-                        String contentType = httpRequest.getContentType(name);
-                        InputStream is = httpRequest.getInputStream(name);
-                        fileItem = new FileItem(IOUtils.toByteArray(is), contentType);
-                        fileItem.setFileName(httpRequest.getFilesystemName(name));
-                        break;
-                    }
-                }
-            }
-        }
-        return fileItem;
-    }
-
-    /**
-     *
-     */
-    protected void init() throws Exception {
-        ConversationState cs = getConversationState();
-
-        // try to identify the usecase. It is either in the request or in the conversation
-        Usecase usecase = getUsecase();
-        if (usecase == null) {
-            log.warn("No usecase (neither 'create' nor 'update' nor 'delete') has been specified!");
-            //throw new UnsupportedOperationException("No usecase (neither 'create' nor 'update' nor 'delete') has been specified!");
-        }
-        List<Usecase> supportedUsecases = Arrays.asList(Usecase.values());
-        if (!supportedUsecases.contains(usecase)) {
-            if (cs == null || !supportedUsecases.contains(cs.getUsecase())){
-                throw new UnsupportedOperationException("The following usecase is not supported by this adapter implementation: " + usecase);
-            }
-            usecase = cs.getUsecase();
-        }
-        
-        String resourcePath = getAdaptedResourcePathFromConversationState();
-
-        // TODO: For creation one doesn't need necessarily a resource path, but a resource type definition would be sufficient, whereas for update/modify and delete one needs a resource path
-        if(resourcePath == null){
-            String adaptedResourceName = getResourceConfigProperty("adapted-resource-name");
-            String adaptedResourceNamespace = getResourceConfigProperty("adapted-resource-namespace");
-            if (adaptedResourceName != null && adaptedResourceNamespace != null) {
-                log.warn("TODO: Implement initialization of resource from resource type definition: " + adaptedResourceName + ", " + adaptedResourceNamespace);
-            }
-            throw new IllegalStateException("The adapted resource path must be specified in the request or should be available in the conversation state");
-        }
-        
-        Object model = null;
-        if (cs == null || cs.getModel() == null) { // This is check is needed because the model is not serializable
-            // Instantiate model, depending on the usecase
-            if (Usecase.create.equals(usecase)) {
-                CreatableV3 cv3 = getAdaptedResourceAsCreatableV3(resourcePath);
-                model = cv3.getResourceInputForCreation();
-            } else if (Usecase.modify.equals(usecase)) {
-                ModifiableV3 mv3 = getAdaptedResourceAsModifiableV3(resourcePath);
-                model = mv3.getResourceInputForModification();
-            } else if (Usecase.remove.equals(usecase)) {
-                DeletableV1 dv1 = getAdaptedResourceAsDeletableV1(resourcePath);
-                model = dv1.getResourceInputForDeletion();
-            } else {
-                log.warn("Could not identify usecase");
-            }
-        } else {
-            model = cs.getModel();
-        }
-        
-        initConversation(model, usecase, resourcePath);
-    }
-
-    /**
-     * Validates and calls the create() or modify() or delete() method
-     */
-    public void commit() throws Exception {
-        java.text.DateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSZ");
-        if( log.isDebugEnabled() )
-            log.debug("Start commit " + dateFormat.format(new java.util.Date()));
-        ConversationState cs = getConversationState();
-        String path = cs.getResourcePath();
-        Usecase usecase = cs.getUsecase();
-        
-        if (Usecase.create.equals(usecase)) {
-            CreatableV3 cv3 = getAdaptedResourceAsCreatableV3(path);
-            ResourceInput input = (ResourceInput) getConversationState().getModel();
-            if(input.validate()){
-                if( log.isDebugEnabled() )
-                    log.debug("Start create " + dateFormat.format(new java.util.Date()));
-                cv3.create(input);
-                if( log.isDebugEnabled() )
-                    log.debug("End create " + dateFormat.format(new java.util.Date()));
-            }else{
-                throw new IllegalArgumentException("The input for the adapted resource is not valid");
-            }
-        } else if (Usecase.modify.equals(usecase)) {
-            ModifiableV3 mv3 = getAdaptedResourceAsModifiableV3(path);
-            ResourceInput input = (ResourceInput) getConversationState().getModel();
-            if(input.validate()){
-                mv3.modify(input);
-            }else{
-                throw new IllegalArgumentException("The input for the adapted resource is not valid");
-            }
-        } else if (Usecase.remove.equals(usecase)) {
-            DeletableV1 dv1 = getAdaptedResourceAsDeletableV1(path);
-            ResourceInput input = (ResourceInput) getConversationState().getModel();
-            if(input.validate()){
-                dv1.delete(input);
-            }else{
-                throw new IllegalArgumentException("The input for the adapted resource is not valid");
-            }
-        }
-        if( log.isDebugEnabled() )
-            log.debug("End commit " + dateFormat.format(new java.util.Date()));
-    }
-
-    /**
-     * If the conversation/continuation has been canceled, then react accordingly
-     */
-    private void doCancel() throws Exception {
-        String adaptedResourcePath = getAdaptedResourcePathFromConversationState();
-        log.debug("Adapted resource: " +  adaptedResourcePath);
-        Resource adaptedResource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), adaptedResourcePath);
-        if (ResourceAttributeHelper.hasAttributeImplemented(adaptedResource, "Viewable", "2") && ResourceAttributeHelper.hasAttributeImplemented(adaptedResource, "Versionable", "2")) {
-            if (((org.wyona.yanel.core.api.attributes.ViewableV2) adaptedResource).exists()) {
-                ((org.wyona.yanel.core.api.attributes.VersionableV2) adaptedResource).cancelCheckout();
-            } else {
-                log.warn("Resource '" + adaptedResourcePath + "' does not exist!");
-            }
-        } else {
-            log.warn("Resource '" + adaptedResourcePath + "' is not ViewableV2/VersionableV2 and hence checkout cannot be canceled!");
-        }
-    }
-
-    /**
-     * Get adapted resource path. It is either in the request or in the conversation.
-     */
-    private String getAdaptedResourcePathFromConversationState() {
-        String adaptedResourcePath = getAdaptedResourcePath();
-        ConversationState cs = getConversationState();
-        if(adaptedResourcePath == null && cs != null) {
-            adaptedResourcePath = cs.getResourcePath();
-        }
-        return adaptedResourcePath;
-    }
-
-    /**
-     * Check if resource has the interface CreatableV3 implemented
-     */
-    private CreatableV3 getAdaptedResourceAsCreatableV3(String path) throws Exception {
-        Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
-        if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Creatable", "3")) {
-            throw new Exception("The adapted resource (" + resource.getResourceTypeUniversalName() + ", " + path + ") is not of the type '" + CreatableV3.class.getName() + "'");
-        }
-        return (CreatableV3) resource;
-    }
-    
-    private ModifiableV3 getAdaptedResourceAsModifiableV3(String path) throws Exception {
-        Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
-        if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Modifiable", "3")) {
-            throw new Exception("The adapted resource is not of the type " + ModifiableV3.class.getName());
-        }
-		return (ModifiableV3) resource;
-    }
-    
-    private DeletableV1 getAdaptedResourceAsDeletableV1(String path) throws Exception {
-        Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
-        if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Deletable", "1")) {
-            throw new Exception("The adapted resource is not of the type " + DeletableV1.class.getName());
-        }
-		return (DeletableV1) resource;
-    }
-
-    protected void passParameters(Transformer transformer) throws Exception {
-        super.passParameters(transformer);
-        ConversationState cs = getConversationState();
-        if (cs != null) {
-            transformer.setParameter("resourceInput", cs.getModel());
-            transformer.setParameter("url.before.conversation", cs.getRefererUrl());
-        } else {
-            log.warn("The conversation was not initialized");
-        }
-    }
-
-    protected void passParameters(JellyContext jellyContext) throws Exception {
-        super.passParameters(jellyContext);
-        ConversationState cs = getConversationState();
-        if (cs != null) {
-            jellyContext.setVariable("resourceInput", cs.getModel());
-            jellyContext.setVariable("url.before.conversation", cs.getRefererUrl());
-        } else {
-            log.warn("The conversation was not initialized");
-        }
-    }
-}

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,469 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import javax.xml.transform.Source;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.sax.SAXResult;
-import javax.xml.transform.sax.SAXTransformerFactory;
-import javax.xml.transform.sax.TransformerHandler;
-import javax.xml.transform.stream.StreamResult;
-import javax.xml.transform.stream.StreamSource;
-
-import org.apache.avalon.framework.configuration.Configuration;
-import org.apache.avalon.framework.configuration.ConfigurationUtil;
-import org.apache.commons.jelly.JellyContext;
-import org.apache.commons.jelly.XMLOutput;
-import org.apache.xml.resolver.tools.CatalogResolver;
-import org.apache.xml.serializer.Serializer;
-import org.w3c.dom.Document;
-import org.wyona.security.core.api.Identity;
-import org.wyona.yanel.core.Constants;
-import org.wyona.yanel.core.attributes.viewable.View;
-import org.wyona.yanel.core.serialization.SerializerFactory;
-import org.wyona.yanel.core.source.SourceResolver;
-import org.wyona.yanel.core.transformation.I18nTransformer2;
-import org.wyona.yanel.core.transformation.XIncludeTransformer;
-import org.wyona.yanel.core.util.PathUtil;
-import org.wyona.yanel.impl.resources.ViewDescriptorUsingTemplate.TemplateOption;
-import org.xml.sax.InputSource;
-import org.xml.sax.XMLReader;
-import org.xml.sax.helpers.XMLReaderFactory;
-
-import org.apache.log4j.Logger;
-
-/**
- * This resource type takes care only of the custom configuration.
- * It behaves much like org.wyona.yanel.impl.resources.usecase.UsecaseResource.
- * In the resource configuration one can specify views and there a Jelly template and arbitrary XSLTs.
- * <p>
- * The templates get the following parameters(subclasses may enrich this set):
- * <ul>
- * <li> <b>All the parameters from the incoming request</b>
- * <li>yanel.path.name
- * <li>yanel.path
- * <li>yanel.back2realm
- * <li>yanel.back2context
- * <li>yanel.global.htdocs
- * <li>yanel.resource.htdocs
- * <li>yanel.requested.language
- * <li>yanel.content.language
- * <li>client (i.e. user agent)
- * <li>yanel.username
- * <li>yanel.toolbar.status
- * <li>yanel.reserved.prefix
- * </ul>
- * <p>
- * The view description looks as follows:
- * <pre>
- * [yanel:custom-config]
- *   [views xmlns="http://www.wyona.org/yanel/rti/1.0"]
- *     [view id="..."]
- *       [template type="JELLY"]...[/template]
- *       [xslt]...[/xslt]*
- *       [mime-type]...[/mime-type]? (e.g. text/html)
- *       [serializer key="..."/]?
- *     [/view]
- *     [screen/]*
- *     ...
- *     [fragment/]*
- * [/yanel:custom-config]
- * </pre>
- * The 'fragment' view hints the view adapters that this is not the user screen but rather a fragment generated by AJAX call or something similar.
- * The 'screen' view is the synonym of 'view', used to stress that the view is not a fragment
- * */
-public abstract class JellyControllerAdapter extends ControllerAdapter {
-    
-    private static Logger log = Logger.getLogger(JellyControllerAdapter.class);
-
-    // Jelly can be configured to escape the text that it gets from JAVA
-    // This parameter allows to disable escaping.
-    // Accepted values: on, off, true, false, yes, no
-    public static final String ESCAPE_TEXT_IN_JELLY = "escape.text.in.jelly";
-    
-    private HashMap<String, ViewDescriptorUsingTemplate> viewDescriptors;
-    
-    public JellyControllerAdapter() {
-        super();
-    }
-    
-    /**
-     * Checks if the parameter that configures the escaping was passed:
-     * <ul>
-     * <li> As a parameter in the request
-     * <li> As a property in the configuration
-     * </ul>
-     * @return <code>true</code> by default, otherwise depends on the parameter value
-     * */
-    public boolean isEscapeTextEnabled(){
-        String escape = getParameterAsString(ESCAPE_TEXT_IN_JELLY);
-        if(escape == null){
-            try {
-                escape = getConfiguration().getProperty(ESCAPE_TEXT_IN_JELLY);
-            } catch (Exception e) {
-                log.info("Could not detect the property "+ESCAPE_TEXT_IN_JELLY+", will use default value");
-            }
-        }
-        
-        if(escape == null){
-            return true;
-        }
-		if("on".equals(escape.trim().toLowerCase()) || "yes".equals(escape.trim().toLowerCase()) || Boolean.valueOf(escape.trim())){
-		    return true;
-		}
-		return false;
-    }
-    
-    /**
-     *
-     */
-    public ViewDescriptorUsingTemplate[] getViewDescriptors() {
-        if (this.viewDescriptors == null) {
-            try {
-                this.viewDescriptors = new HashMap<String, ViewDescriptorUsingTemplate>();
-                
-                // reads views from configuration:
-                if (getConfiguration() != null && getConfiguration().getCustomConfiguration() != null) {
-                    Document customConfigDoc = getConfiguration().getCustomConfiguration();
-                    Configuration config = ConfigurationUtil.toConfiguration(customConfigDoc.getDocumentElement());
-                    Configuration viewsConfig = config.getChild("views");
-
-                    {
-                        ViewDescriptorUsingTemplate defaultView = null;
-                        
-                        // Collect view definitions
-                        Configuration[] viewConfigs = viewsConfig.getChildren("view");
-                        for (int i = 0; i < viewConfigs.length; i++) {
-                            String id = normalize(viewConfigs[i].getAttribute("id"));
-                            
-                            ViewDescriptorUsingTemplate viewDescriptor = new ViewDescriptorUsingTemplate(id, viewConfigs[i]);
-                            
-                            if(Constants.Request.DEFAULT_VIEW_ID.equals(id)){
-                                defaultView = viewDescriptor;
-                            }
-                            
-                            this.viewDescriptors.put(id, viewDescriptor);
-                        }
-                        
-                        if(defaultView == null){
-                            defaultView = new ViewDescriptorUsingTemplate(Constants.Request.DEFAULT_VIEW_ID);
-                            defaultView.setMimeType("application/xml");
-                            this.viewDescriptors.put(Constants.Request.DEFAULT_VIEW_ID, defaultView);
-                        }
-                    }
-                    
-                    {
-                        // Collect fragment definitions
-                        //log.debug("Get all fragments ...");
-                        Configuration[] fragmentConfigs = viewsConfig.getChildren("fragment");
-                        for (int i = 0; i < fragmentConfigs.length; i++) {
-                            String id = normalize(fragmentConfigs[i].getAttribute("id"));
-                            if(log.isDebugEnabled()) log.debug("Fragment ID: " + id);
-                            
-                            ViewDescriptorUsingTemplate viewDescriptor = new ViewDescriptorUsingTemplate(id, fragmentConfigs[i], true);
-                            if(this.viewDescriptors.keySet().contains(id)){
-                                log.warn("The view with ID '" + id + "' will be ignored as the descriptor with such an ID already exists!");
-                            }else{
-                                this.viewDescriptors.put(id, viewDescriptor);
-                            }
-                        }
-                    }
-                }
-            } catch (Exception e) {
-                throw new IllegalArgumentException("Views are not properly configured");
-            }
-        }
-        
-        return this.viewDescriptors.values().toArray(new ViewDescriptorUsingTemplate[this.viewDescriptors.size()]);
-    }
-    
-    /**
-     * Runs the pipeline of templates. You can feed an XML, JELLY or XSLT to the pipe.
-     * Every template will be processed and passed through internationalization and XInclude transformers.
-     * So every subsequent template can react to the expanded(resolved) input.
-     */
-    public View getView(String viewId) throws Exception {
-        View view = new View();
-        
-        viewId = normalize(viewId);
-        
-        ViewDescriptorUsingTemplate viewDescriptor = (ViewDescriptorUsingTemplate)getViewDescriptor(viewId);
-        view.setMimeType(viewDescriptor.getMimeType());
-        
-        try {
-            Serializer serializer = viewDescriptor.getSerializer();
-            Serializer xmlSerializer = SerializerFactory.getSerializer(SerializerFactory.XML);
-            
-            InputStream content = null;
-            
-            TemplateOption [] templates = viewDescriptor.getTemplates();
-            //TODO: don't serialize on each template, rather pipe content handlers
-            for (int i = 0; i < templates.length; i++) {
-                // The first one is JELLY or XML, then every other is XSLT
-                if(i + 1 == templates.length){
-                    // Use configured serializer for the last template only
-                    content = processTemplate(content, templates[i], serializer);
-                }else{
-                    // In the intermediate steps only XML is produced
-                    content = processTemplate(content, templates[i], xmlSerializer);
-                }
-            }
-            
-            // write result into view:
-            view.setInputStream(content);
-        } catch(Exception e) {
-            log.error(e + " (" + getPath() + ", " + getRealm() + ")", e);
-            throw new Exception(e);
-        }
-        
-        return view;
-    }
-    
-    /**
-     * @param content Template may be applied to it, e.g. for transformations
-     * @param template Template
-     * @param serializer Serializer
-     * 
-     * @return result of the processing as a stream
-     */
-    protected InputStream processTemplate(InputStream content, TemplateOption template, Serializer serializer) throws Exception{
-        InputStream result = null;
-        
-        SourceResolver uriResolver = new SourceResolver(this);
-        CatalogResolver catalogResolver = new CatalogResolver();
-        
-        InputStream templateInputStream = getTemplateAsStream(template);
-        
-        if(TemplateOption.TYPE_XSLT.equals(template.getType())){
-            // Here after the required template it is 
-            
-            // create xslt transformer:
-            SAXTransformerFactory tf = (SAXTransformerFactory)TransformerFactory.newInstance();
-            tf.setURIResolver(uriResolver);
-            
-            // attach handler:
-            TransformerHandler th = tf.newTransformerHandler(new StreamSource(templateInputStream));
-            
-            th.getTransformer().setURIResolver(uriResolver);
-            Transformer transformer = th.getTransformer();
-            
-            passParameters(transformer);
-            
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            serializer.setOutputStream(baos);
-            th.setResult(new SAXResult(serializer.asContentHandler()));
-
-            // create reader:
-            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
-            xmlReader.setEntityResolver(catalogResolver);
-            xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
-            xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", th);
-            xmlReader.setContentHandler(th);
-            
-            // execute:
-            xmlReader.parse(new InputSource(content));
-            
-            result = new ByteArrayInputStream(baos.toByteArray());
-        }else if(TemplateOption.TYPE_JELLY.equals(template.getType())){
-            log.debug("Template type: " + template.getType());
-            JellyContext jellyContext = new JellyContext();
-            passParameters(jellyContext);
-
-            ByteArrayOutputStream jellyResultStream = new ByteArrayOutputStream();
-            
-            XMLOutput jellyOutput = XMLOutput.createXMLOutput(jellyResultStream, isEscapeTextEnabled());
-            jellyContext.runScript(new InputSource(templateInputStream), jellyOutput);
-            jellyOutput.flush();
-            result = new ByteArrayInputStream(jellyResultStream.toByteArray());
-        }else{
-            result = templateInputStream;
-        }
-        
-        //
-        // Pass the result through i18n and XInclude transformers
-        //
-
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        serializer.setOutputStream(baos);
-        
-        // create i18n transformer:
-        I18nTransformer2 i18nTransformer = new I18nTransformer2(getI18NCatalogueNames(), getRequestedLanguage(), getRealm().getDefaultLanguage());
-        i18nTransformer.setEntityResolver(catalogResolver);
-
-
-        // create xinclude transformer:
-        XIncludeTransformer xIncludeTransformer = new XIncludeTransformer();
-        xIncludeTransformer.setResolver(uriResolver);
-        
-        // chain everything together (create a pipeline):
-        xIncludeTransformer.setResult(new SAXResult(i18nTransformer));
-        i18nTransformer.setResult(new SAXResult(serializer.asContentHandler()));
-        
-        // create reader and execute
-        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
-        xmlReader.setEntityResolver(catalogResolver);
-        xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
-        
-        xmlReader.setContentHandler(new SAXResult(xIncludeTransformer).getHandler());
-        xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", xIncludeTransformer);
-        
-        xmlReader.setContentHandler(new SAXResult(i18nTransformer).getHandler());
-        xmlReader.parse(new InputSource(result));
-        
-        result = new ByteArrayInputStream(baos.toByteArray());
-        
-        return result;
-    }
-    
-    /**
-     * Tries to get the template as input stream. The template can be either accessible through 
-     * a URL or simply from the repository. 
-     *
-     * @param template TemplateOption containing type and URL
-     */
-    protected InputStream getTemplateAsStream(TemplateOption template)throws Exception{
-        log.debug("Template: " + template.getUrl());
-        InputStream templateInputStream = null;
-        if(!template.getUrl().startsWith("/")){
-            // Resolve the URI into the InputStream
-            Source src = new SourceResolver(this).resolve(template.getUrl(), null);
-            ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            TransformerFactory.newInstance().newTransformer().transform(src, new StreamResult(bos));
-            templateInputStream = new ByteArrayInputStream(bos.toByteArray());
-        }else{
-            // Just take the template from the repository node
-            templateInputStream = getRealm().getRepository().getNode(template.getUrl()).getInputStream();
-        }
-        return templateInputStream;
-    }
-    
-    /**
-     * Pass parameters to Jelly context (Add more parameters in subclasses if needed)
-     */
-    protected void passParameters(JellyContext jellyContext) throws Exception{
-        // Attach all parameters that came with the request, such that templates can make use of them.
-        // NOTE: all parameter values will be of type String. In XSLT: <param name="p" value="'actual_value'"/>
-        for (Iterator i = getParameters().entrySet().iterator(); i.hasNext();) {
-            Map.Entry entry = (Map.Entry) i.next();
-            jellyContext.setVariable(String.valueOf(entry.getKey()), entry.getValue());
-        }
-        
-        // Set general parameters
-        jellyContext.setVariable("resource", this); // TODO: Somehow this doesn't work!
-
-        jellyContext.setVariable("continuation.id", getContinuation().getId());
-        jellyContext.setVariable("continuation.id.key", YANEL_CONTINUATION_ID);
-
-        jellyContext.setVariable("yanel.path.name", org.wyona.commons.io.PathUtil.getName(getPath()));
-        jellyContext.setVariable("yanel.path", getPath());
-        
-        jellyContext.setVariable("yanel.back2realm", PathUtil.backToRealm(getPath()));
-        jellyContext.setVariable("yanel.back2context", PathUtil.backToContext(realm, getPath()));
-        
-        jellyContext.setVariable("yanel.global.htdocs", PathUtil.getGlobalHtdocsPath(this));
-        jellyContext.setVariable("yanel.resource.htdocs", PathUtil.getResourcesHtdocsPath(this));
-        
-        jellyContext.setVariable("yanel.requested.language", getRequestedLanguage());
-        jellyContext.setVariable("yanel.content.language", getContentLanguage());
-        
-        String userAgent = getEnvironment().getRequest().getHeader("User-Agent");
-        String client = getClient(userAgent);
-        if (client != null) jellyContext.setVariable("client", client);
-
-        // username
-        String username = getUsername();
-        if (username != null) jellyContext.setVariable("yanel.username", username);
-
-        // Add toolbar status
-        String toolbarStatus = getToolbarStatus();
-        if (toolbarStatus != null) jellyContext.setVariable("yanel.toolbar.status", toolbarStatus);
-        
-        jellyContext.setVariable("yanel.reserved.prefix", getYanel().getReservedPrefix());
-    }
-    
-    /**
-     * Add more parameters in subclasses if needed
-     * */
-    protected void passParameters(Transformer transformer) throws Exception{
-        // Attach all parameters that came with the request. Templates can make use of them.
-        // NOTE: all parameter values will be of type String. In XSLT: <param name="p" value="'actual_value'"/>
-        for (Iterator i = getParameters().entrySet().iterator(); i.hasNext();) {
-            Map.Entry entry = (Map.Entry) i.next();
-            if (entry.getValue() instanceof String) {
-                String value = (String) entry.getValue();
-                transformer.setParameter(String.valueOf(entry.getKey()), value);
-            } else if(entry.getValue() instanceof String[]){
-                // values separated by a space
-                String separator = " ";
-                
-                StringBuffer finalValue = new StringBuffer();
-                String [] values = (String[]) entry.getValue();
-                for (int j = 0; j < values.length; j++) {
-                    finalValue.append(values[j]);
-                    if(j + 1 != values.length){
-                        finalValue.append(separator);
-                    }
-                }
-                transformer.setParameter(String.valueOf(entry.getKey()), finalValue);
-            } else{
-                // Never happens
-            }
-        }
-        
-        // Set general parameters
-        transformer.setParameter("yanel.path.name", org.wyona.commons.io.PathUtil.getName(getPath()));
-        transformer.setParameter("yanel.path", getPath());
-        
-        transformer.setParameter("yanel.back2realm", PathUtil.backToRealm(getPath()));
-        transformer.setParameter("yanel.back2context", PathUtil.backToContext(realm, getPath()));
-        
-        transformer.setParameter("yanel.global.htdocs", PathUtil.getGlobalHtdocsPath(this));
-        transformer.setParameter("yanel.resource.htdocs", PathUtil.getResourcesHtdocsPath(this));
-        
-        transformer.setParameter("yanel.requested.language", getRequestedLanguage());
-        transformer.setParameter("yanel.content.language", getContentLanguage());
-        
-        String userAgent = getEnvironment().getRequest().getHeader("User-Agent");
-        String client = getClient(userAgent);
-        if (client != null) transformer.setParameter("client", client);
-
-        // username
-        String username = getUsername();
-        if (username != null) transformer.setParameter("yanel.username", username);
-
-        // Add toolbar status
-        String toolbarStatus = getToolbarStatus();
-        if (toolbarStatus != null) transformer.setParameter("yanel.toolbar.status", toolbarStatus);
-        
-        transformer.setParameter("yanel.reserved.prefix", getYanel().getReservedPrefix());
-    }
-    
-    protected final String getUsername() {
-        Identity identity = getEnvironment().getIdentity();
-        if (identity != null) return identity.getUsername();
-        return null;
-    }
-    
-    protected final String getClient(String userAgent) {
-        if (userAgent.indexOf("Firefox") > 0) {
-            return "firefox";
-        } else if (userAgent.indexOf("MSIE") > 0) {
-            return "msie";
-        } else {
-            log.warn("Client could not be recognized: " + userAgent);
-            return null;
-        }
-    }
-    
-    protected final String getToolbarStatus() {
-        // TODO: Use YanelServlet.TOOLBAR_KEY instead "toolbar"!
-        return (String) getEnvironment().getRequest().getSession(true).getAttribute("toolbar");
-    }
-    
-}

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,139 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import javax.xml.transform.Transformer;
-
-import org.apache.commons.jelly.JellyContext;
-
-import org.apache.log4j.Logger;
-
-/**
- * Deals with conversations.
- * A conversation is mapped to a path.
- * */
-//TODO: is path ok as a key of the conversation state
-//TODO[high]: conversation state object must be synchronized, because it is put into the 
-//session and may be accessed by different browser instances
-abstract class JellyConversationAdapter extends JellyControllerAdapter {
-
-    private static Logger log = Logger.getLogger(JellyConversationAdapter.class);
-
-    /**
-     * This is the view where the user should get after all the activities are finished.
-     * */
-    protected static final String DONE_VIEW_ID = "done";
-    
-    /**
-     * This is the view where the user should get when the conversation is canceled.
-     * */
-    protected static final String CANCEL_VIEW_ID = "cancel";
-    
-    /**
-     * This parameter is useful when the referrer url is not enough.
-     * For example, the page may needs to go to some page (other than a referrer) after the conversation has been completed.  
-     * */
-    public static final String PARAM_GOTO_URL = "goto.url";
-    
-    private ConversationState conversationState;
-    
-    public JellyConversationAdapter() {
-        super();
-        supportedViewIds.add(CANCEL_VIEW_ID);
-        supportedViewIds.add(DONE_VIEW_ID);
-    }
-    
-    /**
-     * Initialize or keep up to date the conversation state
-     */
-    protected abstract void init() throws Exception;
-    
-    /**
-     * In this method the conversation should be commited. Data may need to be validated befor commit.
-     */
-    public abstract void commit()throws Exception;
-    
-    
-    /**
-     * Returns the conversation state object. It may be available in the session
-     * or invalidated (no more in the session).
-     * @return - <code>null</code> when called before initConversation(), in other cases the conversation state object (maybe invalidated already)
-     * */
-    protected final ConversationState getConversationState(){
-        if(conversationState == null){
-            conversationState = (ConversationState)getEnvironment().getRequest().getSession(true).getAttribute(getPath());
-        }
-        return conversationState;
-    }
-    
-    /**
-     * Checks if the conversation is already in the session and creates it if necessary
-     * */
-    protected final ConversationState initConversation(Object model, Usecase usecase, String resourcePath){
-        log.warn("Continuation: " + getContinuation().getId());
-
-        ConversationState cs = new ConversationState(model);
-        cs.setUsecase(usecase);
-        cs.setResourcePath(resourcePath);
-        
-        ConversationState current = getConversationState();
-        if (current != null && !current.isInvalidated()) {
-            // Refresh the properties, because they are changing during the conversation
-            current.setModel(cs.getModel());
-            current.setCurrentScreen(cs.getCurrentScreen());
-        } else {
-            cs.setRefererUrl(getEnvironment().getRequest().getHeader("Referer"));
-            // The new conversation state becomes current
-            current = cs;
-        }
-        
-        String gotoUrl = getParameterAsString(PARAM_GOTO_URL);
-        if(gotoUrl != null){
-            current.setGotoUrl(gotoUrl);
-        }
-        
-        log.warn("Attach conversation state for usecase '" + current.getUsecase() + "' to session with id '" + getPath() + "'");
-        getEnvironment().getRequest().getSession(true).setAttribute(getPath(), current);
-        return current;
-    }
-    
-    /**
-     * Removes the conversation state from the session and invalidates the conversation state object.
-     * */
-    protected final void destroyConversation(){
-        ConversationState current = getConversationState();
-        if(current != null){
-            current.invalidate();
-        }
-        getEnvironment().getRequest().getSession(true).removeAttribute(getPath());
-    }
-    
-    /**
-     * No additional parameters passed. Subclasses should consult conversation state for additional parameters
-     */
-    protected void passParameters(JellyContext jellyContext) throws Exception {
-        super.passParameters(jellyContext);
-        ConversationState cs = getConversationState();
-        if (cs != null) {
-            if(cs.getGotoUrl() != null){
-                jellyContext.setVariable(PARAM_GOTO_URL, cs.getGotoUrl());
-            }
-        } else {
-            log.warn("The conversation was not initialized");
-        }
-        
-    }
-    
-    /**
-     * No additional parameters passed. Subclasses should consult conversation state for additional parameters
-     */
-    protected void passParameters(Transformer transformer) throws Exception {
-        super.passParameters(transformer);
-        ConversationState cs = getConversationState();
-        if (cs != null) {
-            if(cs.getGotoUrl() != null){
-                transformer.setParameter(PARAM_GOTO_URL, cs.getGotoUrl());
-            }
-        } else {
-            log.warn("The conversation was not initialized");
-        }
-    }
-}

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,51 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-
-/**
- * The adapter knows how to adapt resources. It works as a facade
- * for adapted resource that are implementing some specific functionality. 
- * E.g. a resource can be creatable, deletable, modifiable...
- * <p>
- * Normally the adapter should be instantiated by Yanel servlet    
- * */
-public interface ResourceAdapter{    
-    public static final String PARAM_ADAPTED_RESOURCE_PATH = "adapted.resource.path";
-    
-    public static enum Usecase{
-        /**
-         * If this usecase is specified, then a resource of the specified type will create something.
-         */
-        create,
-        /**
-         * If this usecase is specified, then a resource of the specified type will modify something.
-         */
-        modify,
-        /**
-         * If this usecase is specified, then a resource of the specified type will delete something.
-         */
-        //TODO: the value must be "delete", but YanelServlet does not allow that?
-        remove;
-        
-        /**
-         * Create the usecase out of the given parameter ignoring the case of the string.
-         * Behaves much like simple valueOf
-         * @return null when the parameter is null, otherwise tries to create the usecase.
-         * */
-        public static Usecase caseInsensitiveValueOf(String usecase){
-            if(usecase == null){
-                return null;
-            }
-            
-            return Usecase.valueOf(usecase.toLowerCase());
-        }
-    }
-    
-    public String getAdaptedResourcePath();
-    public void setAdaptedResourcePath(String adaptedResourcePath);
-    
-    /**
-     * The adapter knows how to adapt the resource for specific usecases
-     * */
-    public Usecase getUsecase();
-    public void setUsecase(Usecase usecase);
-}

Deleted: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java	2010-01-30 14:54:55 UTC (rev 47361)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -1,195 +0,0 @@
-package org.wyona.yanel.impl.resources;
-
-import java.util.Enumeration;
-import java.util.Properties;
-
-import org.apache.avalon.framework.configuration.Configuration;
-import org.apache.avalon.framework.configuration.ConfigurationException;
-import org.apache.xml.serializer.Serializer;
-import org.wyona.commons.io.MimeTypeUtil;
-import org.wyona.yanel.core.attributes.viewable.ViewDescriptor;
-import org.wyona.yanel.core.serialization.SerializerFactory;
-
-/**
- * A view is configured with a template and a set of XSLTs:
- * <pre>
- * [view id="x"]
- *  [template type="JELLY"]...location...[/template]
- *  [xslt]_location_[/xslt]
- *  ...
- *  [xslt]_location_[/xslt]
- *  [mime-type]...(test/html)...[mime-type]
- *  [serializer key="...(XHTML_STRICT)..."]
- *  ... serializer props...
- *  [/serializer]
- *  
- * [/view]
- * </pred>
- * */
-public class ViewDescriptorUsingTemplate extends ViewDescriptor {
-
-    /**
-     *
-     */
-    public static class TemplateOption{
-        public static final String TYPE_JELLY = "JELLY";
-        public static final String TYPE_XSLT = "XSLT";
-        /**
-         * This is to say that it is raw XML
-         */
-        public static final String TYPE_XML = "XML";
-        
-        private String type;
-        private String url;
-        
-        public TemplateOption(String type, String url) {
-            this.type = type;
-            this.url = url;
-        }
-        
-        public String getType() {
-            return type;
-        }
-        
-        public String getUrl() {
-            return url;
-        }
-    }
-    
-    private boolean fragment = false;
-    
-    protected TemplateOption [] templates = new TemplateOption[0];
-    
-    protected String serializerKey = SerializerFactory.XHTML_STRICT_KEY;
-    protected Properties serializerProperties = new Properties();
-
-    protected ViewDescriptorUsingTemplate(String id){
-        super(id);
-    }
-    
-    public ViewDescriptorUsingTemplate(String id, Configuration viewDefinition) throws ConfigurationException{
-        this(id, viewDefinition, false);
-    }
-    
-    public ViewDescriptorUsingTemplate(String id, Configuration viewDefinition, boolean isFragment) throws ConfigurationException{
-        super(id);
-        configure(viewDefinition);
-        this.fragment = isFragment;
-    }
-    
-    /**
-     * Defaults:
-     * <p>
-     * mime-type : text/html<br>
-     * serializer: XHTML_STRICT<br>
-     * serializer props: empty
-     */
-    protected void configure(Configuration config) throws ConfigurationException {
-        Configuration[] templateConfigs = config.getChildren("template");
-        if(templateConfigs.length == 0){
-            throw new ConfigurationException("A template is not specified");
-        }
-        
-        Configuration[] xsltConfigs = config.getChildren("xslt");
-        
-        templates = new TemplateOption[1 + xsltConfigs.length];
-        
-        templates[0] = new TemplateOption(templateConfigs[0].getAttribute("type"), templateConfigs[0].getValue());
-        for (int i = 0; i < xsltConfigs.length; i++) {
-            templates[i+1] = new TemplateOption(TemplateOption.TYPE_XSLT, xsltConfigs[i].getValue());
-        }
-        
-        Configuration mimeTypeConfig = config.getChild("mime-type", false);
-        if (mimeTypeConfig != null) {
-            setMimeType(mimeTypeConfig.getValue());
-        }else{
-            setMimeType("text/html");
-        }
-        
-        Configuration serializerConfig = config.getChild("serializer", false);
-        if (serializerConfig != null) {
-            serializerKey = (serializerConfig.getAttribute("key") == null ? serializerKey : serializerConfig.getAttribute("key"));
-            serializerProperties = new Properties();
-            Configuration propertyConfig = serializerConfig.getChild("omit-xml-declaration", false);
-            if (propertyConfig != null) {
-                serializerProperties.setProperty("omit-xml-declaration", propertyConfig.getValue());
-            }
-            propertyConfig = serializerConfig.getChild("doctype-public", false);
-            if (propertyConfig != null) {
-                serializerProperties.setProperty("doctype-public", propertyConfig.getValue());
-            }
-            propertyConfig = serializerConfig.getChild("doctype-system", false);
-            if (propertyConfig != null) {
-                serializerProperties.setProperty("doctype-sytem", propertyConfig.getValue());
-            }
-        }
-    }
-
-    public String getSerializerKey() {
-        return serializerKey;
-    }
-
-    public void setSerializerKey(String serializerKey) {
-        this.serializerKey = serializerKey;
-    }
-
-    public Properties getSerializerProperties() {
-        return serializerProperties;
-    }
-
-    public void setSerializerProperties(Properties serializerProperties) {
-        this.serializerProperties = serializerProperties;
-    }
-
-    public TemplateOption[] getTemplates() {
-        return templates;
-    }
-    
-    /**
-     * @return <code>true</code> when the view represents a fragment, not a screen.
-     * For instance, a fragment view is useful when doing AJAX call
-     * */
-    public boolean isFragment(){
-        return fragment;
-    }
-    
-    /**
-     * Creates an html or xml serializer for this view descriptor
-     */
-    public Serializer getSerializer() throws Exception {
-        Serializer serializer = null;
-        String serializerKey = getSerializerKey();
-        if (serializerKey != null) {
-            serializer = SerializerFactory.getSerializer(serializerKey);
-            if (serializer == null) {
-                throw new Exception("could not create serializer for key: " + serializerKey);
-            }
-        } else {
-            String mimeType = getMimeType();
-
-            if (MimeTypeUtil.isHTML(mimeType) && !MimeTypeUtil.isXML(mimeType)) {
-                serializer = SerializerFactory.getSerializer(SerializerFactory.HTML_TRANSITIONAL);
-            } else if (MimeTypeUtil.isHTML(mimeType) && MimeTypeUtil.isXML(mimeType)){
-                serializer = SerializerFactory.getSerializer(SerializerFactory.XHTML_STRICT);
-            } else if (MimeTypeUtil.isXML(mimeType)) {
-                serializer = SerializerFactory.getSerializer(SerializerFactory.XML);
-            } else if (MimeTypeUtil.isTextual(mimeType)) {
-                serializer = SerializerFactory.getSerializer(SerializerFactory.TEXT);
-            } else{
-                serializer = SerializerFactory.getSerializer(SerializerFactory.XHTML_STRICT);
-            }
-        }
-        // allow to override xml declaration and doctype:
-        Properties properties = getSerializerProperties();
-        if (properties != null) {
-            Enumeration propNames = properties.propertyNames();
-            while (propNames.hasMoreElements()) {
-                String name = (String)propNames.nextElement();
-                String value = properties.getProperty(name);
-
-                serializer.getOutputFormat().setProperty(name, value);
-            }
-        }
-        return serializer;
-    }
-}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/BasicFormResource.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/BasicFormResource.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/BasicFormResource.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/BasicFormResource.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,324 @@
+package org.wyona.yanel.impl.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map.Entry;
+
+import org.apache.log4j.Logger;
+import org.wyona.yanel.core.ResourceNotFoundException;
+import org.wyona.yanel.core.attributes.viewable.View;
+
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+/**
+ * Base class to handle forms. Returns the following XML streams:
+ * 
+ * <p>Initial call:</p>
+ * <pre>
+ * &lt;form-resource&gt;
+ *   &lt;form/&gt;
+ * &lt;/form-resource&gt;
+ * </pre>
+ * 
+ * <p>Validation failed:</p>
+ * <pre>
+ * &lt;form-resource&gt;
+ *   &lt;form/&gt;
+ *   &lt;validation/&gt;
+ *   &lt;input/&gt;
+ *   &lt;result/&gt;
+ * &lt;/form-resource&gt;
+ * </pre>
+ * 
+ * <p>Successful processing:</p>
+ * <pre>
+ * &lt;form-resource&gt;
+ *   &lt;confirmation/&gt;
+ *   &lt;input/&gt;
+ *   &lt;result/&gt;
+ * &lt;/form-resource&gt;
+ * </pre>
+ * 
+ * <p>Error occurred:</p>
+ * <pre>
+ * &lt;form-resource&gt;
+ *   &lt;error/&gt;
+ *   &lt;input/&gt;
+ *   &lt;result/&gt;
+ * &lt;/form-resource&gt;
+ * </pre>
+ * 
+ * @author Matthias Leumann
+ * 
+ */
+public abstract class BasicFormResource extends BasicXMLResource {
+    
+    private static final Logger log = Logger.getLogger(BasicFormResource.class);
+    
+    private static final String ENCODING = "UTF-8";
+    private static final String INPUT_SUBMIT = "submit";
+    
+    protected static final String RESULT_CODE_DONE_SUCCESSFUL = "done-successful";
+    private static final String RESULT_CODE_FAILED_INVALID = "failed-invalid";
+    private static final String RESULT_CODE_FAILED_ERROR = "failed-error";
+    
+    private String resultCode;
+    private StringBuilder resultXml = new StringBuilder();
+    private LinkedList<NameValuePair> validationErrors = new LinkedList<NameValuePair>();
+
+    public View getView(String viewId) throws Exception {
+        String sslRedirectParam = getResourceConfigProperty("ssl-redirect");
+        if (sslRedirectParam != null && sslRedirectParam.equals("true")) {
+            if (!getEnvironment().getRequest().isSecure() && getEnvironment().getRequest().getParameter("ssl") == null) {
+                View view = new View();
+                view.setResponse(false);
+                URL sslURL;
+                int sslPort = 443;
+                if (getRealm().isProxySet()) {
+                    if (realm.getProxySSLPort() >= 0) sslPort = realm.getProxySSLPort();
+                    String requestURI = getEnvironment().getRequest().getRequestURI();
+                    if (realm.getProxyPrefix() != null) requestURI = requestURI.substring(realm.getProxyPrefix().length());
+                    sslURL = new URL("https", getRealm().getProxyHostName(), sslPort, requestURI + "?ssl=true");
+                } else {
+                    sslURL = new URL("https", getEnvironment().getRequest().getServerName(), sslPort, getEnvironment().getRequest().getRequestURI() + "?ssl=true");
+                }
+                log.warn("Redirect to SSL: " + sslURL.toString());
+                getEnvironment().getResponse().setHeader("Location", sslURL.toString());
+                getEnvironment().getResponse().setStatus(javax.servlet.http.HttpServletResponse.SC_TEMPORARY_REDIRECT);
+                return view;
+            }
+        }
+        return super.getView(viewId);
+    }
+    
+    protected final InputStream getContentXML(String viewId) throws Exception {
+        if (!exists()) {
+            log.warn("No such resource: " + getPath());
+            throw new ResourceNotFoundException("No such resource: " + getPath());
+        }
+        
+        resultXml.append("<form-resource xmlns=\""+getNamespaceURI()+"\">");
+        if (isFormSubmitted()) {
+            validate();
+            if (isInputValid()) {
+                try {
+                    resultCode = execute();
+                    appendConfirmation();
+                } catch (Exception e) {
+                    log.error(e, e);
+                    resultCode = RESULT_CODE_FAILED_ERROR;
+                    appendError(e);
+                }
+            } else {
+                resultCode = RESULT_CODE_FAILED_INVALID;
+                appendForm();
+                appendValidation();
+            }
+            appendInput();
+            appendResult();
+        } else {
+           appendForm();
+        }
+        resultXml.append("</form-resource>");
+        return new ByteArrayInputStream(resultXml.toString().getBytes(ENCODING));
+    }
+    
+    /**
+     * Adds a validation error. Use this method to report invalid input.
+     * 
+     * @param inputName name of the input parameter 
+     * @param validationMessage validation message (may use i18n key)
+     */
+    protected final void addValidationError(String inputName, String validationMessage) {
+        validationErrors.add(new NameValuePair(inputName, validationMessage));
+    }
+    
+    /**
+     * @deprecated Use org.wyona.yanel.core.util.MailUtil.send() instead
+     *
+     * Sends an email. E.g. use this method to send confirmation mails.
+     * 
+     * @param from sender email address
+     * @param replyTo email address (if null, then no reply-to will be set)
+     * @param to receiver email address
+     * @param subject email subject
+     * @param content email content
+     */
+    protected void sendMail(String from, String replyTo, String to, String subject, String content) throws Exception {
+        log.warn("DEPRECATED");
+        org.wyona.yanel.core.util.MailUtil.send(from, replyTo, to, subject, content);
+
+
+/*
+        // Create a mail session
+        java.util.Properties props = new java.util.Properties();
+        props.put("mail.smtp.host", getResourceConfigProperty("smtpHost"));
+        props.put("mail.smtp.port", "" + getResourceConfigProperty("smtpPort"));
+        // TODO: http://java.sun.com/products/javamail/javadocs/javax/mail/Session.html
+        Session session = Session.getDefaultInstance(props, null);
+
+        // Construct the message
+        Message msg = new MimeMessage(session);
+        msg.setFrom(new InternetAddress(from));
+        if (replyTo != null) {
+            InternetAddress[] replyToAddresses = new InternetAddress[1];
+            replyToAddresses[0] = new InternetAddress(replyTo);
+            msg.setReplyTo(replyToAddresses);
+        }
+        if( log.isDebugEnabled() )
+            log.debug("From: " + msg.getFrom() + ", " + from);
+        msg.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
+        msg.setSubject(subject);
+        msg.setText(content);
+*/
+
+/*
+        msg.setSubject(subject,"utf-8");
+        msg.setContent(content, "text/plain;charset=utf-8");
+*/
+
+/*
+        // Send the message
+        Transport.send(msg);
+*/
+    }
+    
+    /**
+     * Generates valid XML containing the data to display the form screen.
+     * 
+     * @return valid XML string
+     */
+    protected abstract String generateForm() throws Exception;
+    
+    /**
+     * Generates valid XML containing the data to display the confirmation screen.
+     * 
+     * @return valid XML string
+     */
+    protected abstract String generateConfirmation() throws Exception;
+    
+    /**
+     * Validates the form input. Use the method <code>addValidationError</code> to report invalid input.
+     */
+    protected abstract void validate() throws Exception;
+    
+    /**
+     * Executes custom operation if the form input was successfully validated.
+     * 
+     * @return result code (e.g. "done-successful")
+     * @throws Exception if processing failed.
+     */
+    protected abstract String execute() throws Exception;
+    
+    /**
+     * @return the name space URI of of the resulting XML.
+     */
+    protected abstract String getNamespaceURI();
+    
+    
+    /**
+     * @return returns true if and only if the input parameters contain a parameter "submit".
+     */
+    private boolean isFormSubmitted() {
+        return getParameterAsString(INPUT_SUBMIT) != null;
+    }
+    
+    /**
+     * @return true if and only if there are no validation errors.
+     */
+    private boolean isInputValid() {
+        return validationErrors.isEmpty();
+    }
+    
+    /**
+     * Appends the result code to the result XML.
+     */
+    private void appendResult() {
+        resultXml.append("<result>");
+        resultXml.append(resultCode);
+        resultXml.append("</result>");
+    }
+    
+    /**
+     * Appends the data for the form screen to the result XML.
+     */
+    private void appendForm() throws Exception {
+        resultXml.append("<form>");
+        String form = generateForm();
+        if (form != null) {
+            resultXml.append(form);
+        }
+        resultXml.append("</form>");
+    }
+    
+    /**
+     * Appends the data for the confirmation screen to the result XML.
+     */
+    private void appendConfirmation() throws Exception {
+        resultXml.append("<confirmation>");
+        String confirmation = generateConfirmation();
+        if (confirmation != null) {
+            resultXml.append(confirmation);
+        }
+        resultXml.append("</confirmation>");
+    }
+    
+    /**
+     * Appends the validation messages to the result XML.
+     */
+    private void appendValidation() {
+        resultXml.append("<validation>");
+        for (NameValuePair error : validationErrors) {
+            resultXml.append("<"+error.getName()+">"+error.getValue()+"</"+error.getName()+">");
+        }
+        resultXml.append("</validation>");
+    }
+    
+    /**
+     * Appends the form input to the result XML.
+     */
+    private void appendInput() {
+        resultXml.append("<input>");
+        Iterator it = getParameters().entrySet().iterator();
+        while (it.hasNext()) {
+            Entry param = (Entry)it.next();
+            resultXml.append("<"+param.getKey()+"><![CDATA["+param.getValue()+"]]></"+param.getKey()+">");
+        }
+        resultXml.append("</input>");
+    }
+    
+    private void appendError(Exception e) {
+        resultXml.append("<error>");
+        resultXml.append("<![CDATA[" + e.toString() + "]]>");
+        resultXml.append("</error>");
+    }
+    
+    /**
+     * Private utility class to add validation errors to the list.
+     */
+    private class NameValuePair {
+        private String name;
+        private String value;
+        
+        public NameValuePair(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+        
+        public String getName() {
+            return name;
+        }
+        
+        public String getValue() {
+            return value;
+        }
+    }
+
+}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/Continuation.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/Continuation.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/Continuation.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/Continuation.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,31 @@
+package org.wyona.yanel.impl.resources;
+
+/**
+ *
+ */
+public class Continuation {
+
+    private String id;
+
+    /**
+     *
+     */
+    public Continuation() {
+        // TODO: Use UUID
+        id = "" + new java.util.Date().getTime();
+    }
+
+    /**
+     *
+     */
+    public Continuation(String id) {
+        this.id = id;
+    }
+
+    /**
+     *
+     */
+    public String getId() {
+        return id;
+    }
+}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ControllerAdapter.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ControllerAdapter.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ControllerAdapter.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ControllerAdapter.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,156 @@
+package org.wyona.yanel.impl.resources;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.wyona.yanel.core.Constants;
+import org.wyona.yanel.core.Resource;
+import org.wyona.yanel.core.api.attributes.ViewableV2;
+import org.wyona.yanel.core.attributes.viewable.ViewDescriptor;
+
+abstract class ControllerAdapter extends Resource implements ResourceAdapter, ViewableV2 {
+    private static Logger log = Logger.getLogger(ControllerAdapter.class);
+    public static String YANEL_CONTINUATION_ID = "yanel.continuation.id";
+    
+    /**
+     * Resource config property for internationalization
+     * */
+    //TODO: what is that???
+    private static final String I18N_CATALOG_PROPERTY = "i18n-catalogue";
+
+    protected final Set<String> supportedViewIds = new HashSet<String>();
+    
+    private String adaptedResourcePath = null;
+    
+    private Usecase usecase = null;
+    
+    /**
+     * Constants.Request.DEFAULT_VIEW_ID is registered
+     */
+    public ControllerAdapter() {
+        supportedViewIds.add(Constants.Request.DEFAULT_VIEW_ID);
+    }
+    
+    /**
+     * Checks if the path was set, otherwise
+     * looks into available parameters and returns the value of
+     * PARAM_ADAPTED_RESOURCE_PATH
+     * @return null when the parameter is not found
+     * */
+    public final String getAdaptedResourcePath() {
+        if(adaptedResourcePath == null){
+            adaptedResourcePath = getParameterAsString(PARAM_ADAPTED_RESOURCE_PATH); 
+        }
+        return adaptedResourcePath;
+    }
+    
+    public void setAdaptedResourcePath(String adaptedResourcePath) {
+        this.adaptedResourcePath = adaptedResourcePath;
+    }
+    
+    /**
+     * Checks if the usecase was set, otherwise looks into available parameters
+     * @return null when usecase can't be determined
+     * */
+    public final Usecase getUsecase() {
+        if(usecase == null){
+            String u = getParameterAsString(Constants.Request.YANEL_RESOURCE_USECASE);
+            if (u == null) {
+                try {
+                    u = getResourceConfigProperty("usecase");
+                } catch (Exception e) {
+                    log.error(e, e);
+                }
+                if (u == null) {
+                    log.error("No usecase (neither 'create' nor 'update/modify' nor 'remove/delete') has been specified (neither within conversation nor query string nor resource config)!");
+                    return null;
+                }
+            }
+            usecase = Usecase.caseInsensitiveValueOf(u);
+        }
+        return usecase;
+    }
+    
+    public void setUsecase(Usecase usecase) {
+        this.usecase = usecase;
+    }
+    
+    /**
+     * Get view descriptor
+     * @param viewId For example 'video'
+     */
+    public ViewDescriptor getViewDescriptor(String viewId) {
+        ViewDescriptor[] viewDescriptors = getViewDescriptors();
+        for (int i = 0; i < viewDescriptors.length; i++) {
+            if (viewDescriptors[i].getId().equalsIgnoreCase(viewId)) {
+                return viewDescriptors[i];
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Checks for validity of the viewId. For instance,
+     * "CANCEL" is the same as "cancel". So this method normalizes
+     * the view id.
+     * <p>
+     * When the client is doing some logic for some view, it first should normalize it with this method
+     * 
+     * @return the normalized viewId (lowercase), when the viewId is <code>null</code> it returns the default view id
+     * */
+    protected final String normalize(String viewId) {
+        if(viewId == null || "".equals(viewId.trim())){
+            return Constants.Request.DEFAULT_VIEW_ID;
+        }
+        for (Iterator i = supportedViewIds.iterator(); i.hasNext();) {
+            String id = (String) i.next();
+            if(id.equalsIgnoreCase(viewId)){
+                return id;
+            }
+        }
+        return viewId.toLowerCase();
+    }
+
+    /**
+     * Gets the names of the i18n message catalogues used for the i18n transformation.
+     * Looks for an rc config property named 'i18n-catalogue'. Defaults to 'global'.
+     * @return i18n catalogue name
+     */
+    protected String[] getI18NCatalogueNames() throws Exception {
+        String[] catalogueNames = getResourceConfigProperties(I18N_CATALOG_PROPERTY);
+        if (catalogueNames == null || catalogueNames.length == 0) {
+            catalogueNames = new String[1];
+            catalogueNames[0] = "global";
+        }
+        return catalogueNames;
+    }
+    
+    public long getSize() throws Exception {
+        return -1;
+    }
+    
+    public boolean exists() throws Exception {
+        return true;
+    }
+
+    /**
+     * Returns the current continuation associated with the request which is associated with this resource, or if the request does not have a continuation, creates one.
+     */
+    public Continuation getContinuation() {
+        if (getRequest().getParameter(YANEL_CONTINUATION_ID) != null) {
+            String continuationId = getRequest().getParameter(YANEL_CONTINUATION_ID);
+            log.warn("Return existing continuation: " + continuationId);
+            return new Continuation(continuationId);
+            //return new ContinuationSessionImpl(continuationId, getSession(true));
+            //return new ContinuationYarepRepoImpl(continuationId, continuationRepo);
+        } else {
+            Continuation continuation = new Continuation();
+            //Continuation continuation = new ContinuationSessionImpl(getSession(true));
+            //Continuation continuation = new ContinuationYarepRepoImpl(continuationRepo);
+            log.warn("New continuation created: " + continuation.getId());
+            return continuation;
+        }
+    }
+}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ConversationState.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ConversationState.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ConversationState.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ConversationState.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,108 @@
+package org.wyona.yanel.impl.resources;
+
+import java.io.Serializable;
+
+import org.wyona.yanel.impl.resources.ResourceAdapter.Usecase;
+
+/**
+ * Conversation state is stored in the session.
+ * During the conversation with the user the model is modified.
+ * However, the model is transient.
+ */
+class ConversationState implements Serializable{
+	private static final long serialVersionUID = 1L;
+
+	//private static final long serialVersionUID = 1724827714145174641L;
+    private Usecase usecase;
+
+    /**
+     * The path to the adapted resource, e.g. Creatable, Modifiable
+     */
+    private String resourcePath;
+    private String refererUrl;
+    private String gotoUrl;
+    private String currentViewId;
+    private String previousViewId;
+    private transient Object model;
+    private boolean invalidated;
+    
+    public ConversationState(Object model){
+        this.model = model;
+    }
+    
+    public ConversationState(String currentViewId, Object model){
+        this.currentViewId = currentViewId;
+        this.model = model;
+    }
+    
+    public String getCurrentScreen() {
+        return currentViewId;
+    }
+
+    public String getPreviousScreen() {
+        return previousViewId;
+    }
+    
+    public Object getModel() {
+        return model;
+    }
+    
+    public String getRefererUrl() {
+        return refererUrl;
+    }
+
+    void setRefererUrl(String refererUrl) {
+        this.refererUrl = refererUrl;
+        if(gotoUrl == null){
+            this.gotoUrl = this.refererUrl;
+        }
+    }
+
+    public String getGotoUrl() {
+        return gotoUrl;
+    }
+    
+    public void setGotoUrl(String gotoUrl) {
+        this.gotoUrl = gotoUrl;
+    }
+    
+    /**
+     * Sets the previous and current view id
+     * */
+    void setCurrentScreen(String currentViewId) {
+        this.previousViewId = this.currentViewId;
+        this.currentViewId = currentViewId;
+    }
+
+    void setModel(Object model) {
+        this.model = model;
+    }
+    
+    void invalidate(){
+        invalidated = true;
+    }
+    
+    /**
+     * Invalidated conversation state means that the state is removed from 
+     * the session
+     * */
+    public boolean isInvalidated(){
+        return invalidated;
+    }
+
+    public Usecase getUsecase() {
+        return usecase;
+    }
+
+    public void setUsecase(Usecase usecase) {
+        this.usecase = usecase;
+    }
+
+    public String getResourcePath() {
+        return resourcePath;
+    }
+
+    public void setResourcePath(String resourcePath) {
+        this.resourcePath = resourcePath;
+    }
+}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyAdapterForCUDResource.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyAdapterForCUDResource.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyAdapterForCUDResource.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyAdapterForCUDResource.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,451 @@
+package org.wyona.yanel.impl.resources;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.transform.Transformer;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.jelly.JellyContext;
+import org.wyona.yanel.core.Resource;
+import org.wyona.yanel.core.api.attributes.CreatableV3;
+import org.wyona.yanel.core.api.attributes.DeletableV1;
+import org.wyona.yanel.core.api.attributes.ModifiableV3;
+import org.wyona.yanel.core.api.attributes.creatable.ResourceInput;
+import org.wyona.yanel.core.api.attributes.creatable.ResourceInputItem;
+import org.wyona.yanel.core.api.attributes.creatable.ResourceInputItemCollection;
+import org.wyona.yanel.core.attributes.viewable.View;
+import org.wyona.yanel.core.util.ResourceAttributeHelper;
+import org.wyona.yanel.impl.jelly.FileItem;
+import org.wyona.yanel.servlet.communication.HttpRequest;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Adapts a CreatableV3.
+ * <p>
+ * The templates in addition get the following parameters:
+ * <ul>
+ * <li>resourceInput - description of the inputs that creatable resource needs
+ * <li>url.before.conversation - the VIEW can redirect here when the conversation is cancelled
+ * <li>resource.input.collection.action - Decides on what kind of action is taken on collection items. Can be 'add' (default) or 'remove'. 
+ * </ul>
+ * 
+ * This is quite a generic class. Subclasses may want to pass other parameters to the templates.
+ * */
+public class JellyAdapterForCUDResource extends JellyConversationAdapter {
+
+    private static Logger log = Logger.getLogger(JellyAdapterForCUDResource.class);
+
+    // Collection items are special. Values can be removed or added/set to specific index
+    public static final String REMOVE_ACTION = "remove";
+    public static final String PARAM_RESOURCE_INPUT_COLLECTION_ACTION = "resource.input.collection.action";
+    public static final String PARAM_RESOURCE_INPUT_COLLECTION_INDEX = "resource.input.collection.index";
+    
+    // The three parameters must be specified together!
+    public static final String PARAM_RESOURCE_INPUT_COLLECTION_OLD_INDEX = "resource.input.collection.old.index";
+    public static final String PARAM_RESOURCE_INPUT_COLLECTION_NEW_INDEX = "resource.input.collection.new.index";
+    public static final String PARAM_TARGET_RESOURCE_INPUT = "target.resource.input";
+    
+    public JellyAdapterForCUDResource() {
+    }
+
+    /**
+     *
+     */
+    public View getView(String viewId)throws Exception{
+        try{
+            ViewDescriptorUsingTemplate viewDescriptor = (ViewDescriptorUsingTemplate)getViewDescriptor(normalize(viewId));
+            if(viewDescriptor == null){
+                throw new IllegalArgumentException("The view descriptor was not found for the given id: '" + viewId + "'");
+            }
+            log.debug("View ID: " + viewDescriptor.getId());
+            
+            if (CANCEL_VIEW_ID.equals(viewDescriptor.getId())) {
+                log.warn("Cancel ...");
+                doCancel();
+                destroyConversation();
+                return super.getView(CANCEL_VIEW_ID);
+            }
+            
+            // Deal with the input
+            init();
+            ConversationState cs = getConversationState();
+            
+            boolean incomingInputIsValid = true;
+            if (cs == null) {
+                throw new IllegalStateException("Conversation state is not available");
+            }
+			String oldIndex = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_OLD_INDEX);
+			String newIndex = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_NEW_INDEX);
+			String [] targetInputs = getParameterAsStringValues(PARAM_TARGET_RESOURCE_INPUT);
+			
+			if(oldIndex != null && newIndex != null){
+			    try {
+			        int o = Integer.parseInt(oldIndex);
+			        int n = Integer.parseInt(newIndex);
+			        
+			        ResourceInput ri = (ResourceInput)cs.getModel();
+			        
+			        // Moving input values
+			        for (String target : targetInputs) {
+			            ResourceInputItem item = ri.getItem(target);
+			            if (item instanceof ResourceInputItemCollection) {
+			                ResourceInputItemCollection itemAsCollection = (ResourceInputItemCollection) item;
+			                itemAsCollection.moveValue(o, n);
+			            }
+			        }
+			    } catch (NumberFormatException e) {
+			        log.warn("oldIndex="+oldIndex+", newIndex="+newIndex+": Illegal indexes passed. Operation skiped");
+			    }
+			}else{
+			    // Fill incoming values and validate. This will also 
+			    incomingInputIsValid = fillIncomingInput(cs);
+			}
+            
+            String nextViewId = viewDescriptor.getId();
+            
+            
+            if(!incomingInputIsValid){
+                if(!viewDescriptor.isFragment()){
+                    // When the screen was not valid go to the previous screen
+                    nextViewId = cs.getPreviousScreen();
+                }else{
+                }
+                // Keep the same screen for the fragment
+                cs.setCurrentScreen(cs.getPreviousScreen());
+            }else {
+                if(!viewDescriptor.isFragment()){
+                    // Now the current screen is the next view
+                    String previous = cs.getPreviousScreen();
+                    cs.setCurrentScreen(nextViewId);
+                    if (DONE_VIEW_ID.equals(nextViewId)) {
+                        // NOTE: assumes that DONE_VIEW_ID is not a fragment
+                        try {
+                            commit();
+                            destroyConversation();
+                        } catch (IllegalArgumentException e) {
+                            log.warn("Cannot commit", e);
+                            //thrown when the ResourceInput is not valid, then go back
+                            nextViewId = previous; 
+                            cs.setCurrentScreen(previous);
+                        }
+                    }
+                }else{
+                    // Keep the previous screen when the input is a fragment
+                    cs.setCurrentScreen(cs.getPreviousScreen());
+                }
+            }
+            return super.getView(nextViewId);
+        } catch(Throwable e){
+            // Cancel the conversation when something goes wrong
+            destroyConversation();
+            
+            throw new Exception("Due to an exception the request has been canceled. Exception message: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Try to set the resource input values according to what came in HTTP
+     * request.
+     * <p>
+     * NOTE[very important]: this method relies on the fact that HTTP request 
+     * has parameters even for checkboxes and select(multiple), which are normally not submitted
+     * when nothing is selected or checked. The "nothing is selected" means that the 
+     * parameter is passed but the value is <code>null</code>. 
+     * This must be ensured in order to use the adapter correctly, e.g. the simple tag library is 
+     * rendering an additional HIDDEN field for this purpose.
+     * @return true when all the incoming input items are valid
+     */
+    private boolean fillIncomingInput(ConversationState cs) throws Exception {
+        ResourceInput ri = (ResourceInput) cs.getModel();
+        String[] itemNames = ri.getItemNames();
+        
+        Set incomingParameterNames = new HashSet(getParameters().keySet());
+        // Add incoming names of the inputs that are files
+        if (getEnvironment().getRequest() instanceof HttpRequest) {
+            HttpRequest httpRequest = (HttpRequest) request;
+            if (httpRequest.isMultipartRequest()) {
+                Enumeration params = httpRequest.getFileNames();
+                while (params.hasMoreElements()) {
+                    incomingParameterNames.add(params.nextElement());
+                }
+            }
+        }
+        //Now we have all incoming parameter names
+        boolean inputValid = true;
+        for (int i = 0; i < itemNames.length; i++) {
+            String action = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_ACTION);
+            String index = getParameterAsString(PARAM_RESOURCE_INPUT_COLLECTION_INDEX);
+            
+            if(!incomingParameterNames.contains(itemNames[i])){
+                // We need to fill only the parameters that were passed
+                continue;
+            }
+            
+            // Go further to addValue()/removeValue()/setValue()
+            
+            Object paramValue = getParameterAsFileItem(itemNames[i]);
+            if(paramValue == null){
+                paramValue = getParameter(itemNames[i]);
+            }
+            
+            if (REMOVE_ACTION.equals(action)){
+                if (ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_COLLECTION) {
+                    ResourceInputItemCollection collectionItem = (ResourceInputItemCollection) ri.getItem(itemNames[i]);
+                    
+                    // Remove the value (maybe at the specified index)
+                    boolean removed = false;
+                    if(paramValue != null){
+                        removed = collectionItem.removeValue(paramValue);
+                    }
+                    if(!removed && index != null){
+                        try{
+                            int valueIndex = Integer.parseInt(index);
+                            collectionItem.removeValue(valueIndex);
+                        }catch(NumberFormatException e){
+                            log.warn(itemNames[i]+"="+paramValue+": Could not remove the parameter at the index "+index);
+                        }
+                    }
+                }else{
+                    ri.getItem(itemNames[i]).setValue(null);
+                }
+            }else{
+                if(ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_FILE_UPLOAD){
+                    FileItem fi = (FileItem)paramValue;
+                    if(fi == null || !fi.hasData()){
+                        // This will cause the old data (maybe null) to be left and validated
+                        inputValid = ri.getItem(itemNames[i]).validate() && inputValid;
+                        continue;
+                    }
+                }
+                
+                if (ri.getItem(itemNames[i]).getType() == ResourceInputItem.INPUT_TYPE_COLLECTION) {
+                    ResourceInputItemCollection collectionItem = (ResourceInputItemCollection) ri.getItem(itemNames[i]);
+                    if(index != null){
+                        try{
+                            int valueIndex = Integer.parseInt(index);
+                            collectionItem.addValue(valueIndex, paramValue);
+                        }catch(NumberFormatException e){
+                            log.warn(itemNames[i]+"="+paramValue+": Could not add the parameter at the index "+index+", appending");
+                            collectionItem.addValue(paramValue);
+                        }
+                    }else{
+                        collectionItem.addValue(paramValue);
+                    }
+                } else {
+                    ri.getItem(itemNames[i]).setValue(paramValue);
+                }
+            }
+            inputValid = ri.getItem(itemNames[i]).validate() && inputValid;
+        }
+        
+        return inputValid;
+    }
+    
+    /**
+     * @return <code>null</code> when the parameter is not a file upload, otherwise a constructed file item.
+     * */
+    private FileItem getParameterAsFileItem(String parameterName)throws Exception{
+        FileItem fileItem = null;
+        if (getEnvironment().getRequest() instanceof HttpRequest) {
+            HttpRequest httpRequest = (HttpRequest) request;
+            if (httpRequest.isMultipartRequest()) {
+                Enumeration fileParameterNames = httpRequest.getFileNames();
+                while (fileParameterNames.hasMoreElements()) {
+                    // create new file item
+                    String name = (String) fileParameterNames.nextElement();
+                    if (name.equals(parameterName)) {
+                        String contentType = httpRequest.getContentType(name);
+                        InputStream is = httpRequest.getInputStream(name);
+                        fileItem = new FileItem(IOUtils.toByteArray(is), contentType);
+                        fileItem.setFileName(httpRequest.getFilesystemName(name));
+                        break;
+                    }
+                }
+            }
+        }
+        return fileItem;
+    }
+
+    /**
+     *
+     */
+    protected void init() throws Exception {
+        ConversationState cs = getConversationState();
+
+        // try to identify the usecase. It is either in the request or in the conversation
+        Usecase usecase = getUsecase();
+        if (usecase == null) {
+            log.warn("No usecase (neither 'create' nor 'update' nor 'delete') has been specified!");
+            //throw new UnsupportedOperationException("No usecase (neither 'create' nor 'update' nor 'delete') has been specified!");
+        }
+        List<Usecase> supportedUsecases = Arrays.asList(Usecase.values());
+        if (!supportedUsecases.contains(usecase)) {
+            if (cs == null || !supportedUsecases.contains(cs.getUsecase())){
+                throw new UnsupportedOperationException("The following usecase is not supported by this adapter implementation: " + usecase);
+            }
+            usecase = cs.getUsecase();
+        }
+        
+        String resourcePath = getAdaptedResourcePathFromConversationState();
+
+        // TODO: For creation one doesn't need necessarily a resource path, but a resource type definition would be sufficient, whereas for update/modify and delete one needs a resource path
+        if(resourcePath == null){
+            String adaptedResourceName = getResourceConfigProperty("adapted-resource-name");
+            String adaptedResourceNamespace = getResourceConfigProperty("adapted-resource-namespace");
+            if (adaptedResourceName != null && adaptedResourceNamespace != null) {
+                log.warn("TODO: Implement initialization of resource from resource type definition: " + adaptedResourceName + ", " + adaptedResourceNamespace);
+            }
+            throw new IllegalStateException("The adapted resource path must be specified in the request or should be available in the conversation state");
+        }
+        
+        Object model = null;
+        if (cs == null || cs.getModel() == null) { // This is check is needed because the model is not serializable
+            // Instantiate model, depending on the usecase
+            if (Usecase.create.equals(usecase)) {
+                CreatableV3 cv3 = getAdaptedResourceAsCreatableV3(resourcePath);
+                model = cv3.getResourceInputForCreation();
+            } else if (Usecase.modify.equals(usecase)) {
+                ModifiableV3 mv3 = getAdaptedResourceAsModifiableV3(resourcePath);
+                model = mv3.getResourceInputForModification();
+            } else if (Usecase.remove.equals(usecase)) {
+                DeletableV1 dv1 = getAdaptedResourceAsDeletableV1(resourcePath);
+                model = dv1.getResourceInputForDeletion();
+            } else {
+                log.warn("Could not identify usecase");
+            }
+        } else {
+            model = cs.getModel();
+        }
+        
+        initConversation(model, usecase, resourcePath);
+    }
+
+    /**
+     * Validates and calls the create() or modify() or delete() method
+     */
+    public void commit() throws Exception {
+        java.text.DateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSZ");
+        if( log.isDebugEnabled() )
+            log.debug("Start commit " + dateFormat.format(new java.util.Date()));
+        ConversationState cs = getConversationState();
+        String path = cs.getResourcePath();
+        Usecase usecase = cs.getUsecase();
+        
+        if (Usecase.create.equals(usecase)) {
+            CreatableV3 cv3 = getAdaptedResourceAsCreatableV3(path);
+            ResourceInput input = (ResourceInput) getConversationState().getModel();
+            if(input.validate()){
+                if( log.isDebugEnabled() )
+                    log.debug("Start create " + dateFormat.format(new java.util.Date()));
+                cv3.create(input);
+                if( log.isDebugEnabled() )
+                    log.debug("End create " + dateFormat.format(new java.util.Date()));
+            }else{
+                throw new IllegalArgumentException("The input for the adapted resource is not valid");
+            }
+        } else if (Usecase.modify.equals(usecase)) {
+            ModifiableV3 mv3 = getAdaptedResourceAsModifiableV3(path);
+            ResourceInput input = (ResourceInput) getConversationState().getModel();
+            if(input.validate()){
+                mv3.modify(input);
+            }else{
+                throw new IllegalArgumentException("The input for the adapted resource is not valid");
+            }
+        } else if (Usecase.remove.equals(usecase)) {
+            DeletableV1 dv1 = getAdaptedResourceAsDeletableV1(path);
+            ResourceInput input = (ResourceInput) getConversationState().getModel();
+            if(input.validate()){
+                dv1.delete(input);
+            }else{
+                throw new IllegalArgumentException("The input for the adapted resource is not valid");
+            }
+        }
+        if( log.isDebugEnabled() )
+            log.debug("End commit " + dateFormat.format(new java.util.Date()));
+    }
+
+    /**
+     * If the conversation/continuation has been canceled, then react accordingly
+     */
+    private void doCancel() throws Exception {
+        String adaptedResourcePath = getAdaptedResourcePathFromConversationState();
+        log.debug("Adapted resource: " +  adaptedResourcePath);
+        Resource adaptedResource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), adaptedResourcePath);
+        if (ResourceAttributeHelper.hasAttributeImplemented(adaptedResource, "Viewable", "2") && ResourceAttributeHelper.hasAttributeImplemented(adaptedResource, "Versionable", "2")) {
+            if (((org.wyona.yanel.core.api.attributes.ViewableV2) adaptedResource).exists()) {
+                ((org.wyona.yanel.core.api.attributes.VersionableV2) adaptedResource).cancelCheckout();
+            } else {
+                log.warn("Resource '" + adaptedResourcePath + "' does not exist!");
+            }
+        } else {
+            log.warn("Resource '" + adaptedResourcePath + "' is not ViewableV2/VersionableV2 and hence checkout cannot be canceled!");
+        }
+    }
+
+    /**
+     * Get adapted resource path. It is either in the request or in the conversation.
+     */
+    private String getAdaptedResourcePathFromConversationState() {
+        String adaptedResourcePath = getAdaptedResourcePath();
+        ConversationState cs = getConversationState();
+        if(adaptedResourcePath == null && cs != null) {
+            adaptedResourcePath = cs.getResourcePath();
+        }
+        return adaptedResourcePath;
+    }
+
+    /**
+     * Check if resource has the interface CreatableV3 implemented
+     */
+    private CreatableV3 getAdaptedResourceAsCreatableV3(String path) throws Exception {
+        Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
+        if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Creatable", "3")) {
+            throw new Exception("The adapted resource (" + resource.getResourceTypeUniversalName() + ", " + path + ") is not of the type '" + CreatableV3.class.getName() + "'");
+        }
+        return (CreatableV3) resource;
+    }
+    
+    private ModifiableV3 getAdaptedResourceAsModifiableV3(String path) throws Exception {
+        Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
+        if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Modifiable", "3")) {
+            throw new Exception("The adapted resource is not of the type " + ModifiableV3.class.getName());
+        }
+		return (ModifiableV3) resource;
+    }
+    
+    private DeletableV1 getAdaptedResourceAsDeletableV1(String path) throws Exception {
+        Resource resource = getYanel().getResourceManager().getResource(getEnvironment(), getRealm(), path);
+        if (!ResourceAttributeHelper.hasAttributeImplemented(resource, "Deletable", "1")) {
+            throw new Exception("The adapted resource is not of the type " + DeletableV1.class.getName());
+        }
+		return (DeletableV1) resource;
+    }
+
+    protected void passParameters(Transformer transformer) throws Exception {
+        super.passParameters(transformer);
+        ConversationState cs = getConversationState();
+        if (cs != null) {
+            transformer.setParameter("resourceInput", cs.getModel());
+            transformer.setParameter("url.before.conversation", cs.getRefererUrl());
+        } else {
+            log.warn("The conversation was not initialized");
+        }
+    }
+
+    protected void passParameters(JellyContext jellyContext) throws Exception {
+        super.passParameters(jellyContext);
+        ConversationState cs = getConversationState();
+        if (cs != null) {
+            jellyContext.setVariable("resourceInput", cs.getModel());
+            jellyContext.setVariable("url.before.conversation", cs.getRefererUrl());
+        } else {
+            log.warn("The conversation was not initialized");
+        }
+    }
+}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyControllerAdapter.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyControllerAdapter.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyControllerAdapter.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyControllerAdapter.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,469 @@
+package org.wyona.yanel.impl.resources;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationUtil;
+import org.apache.commons.jelly.JellyContext;
+import org.apache.commons.jelly.XMLOutput;
+import org.apache.xml.resolver.tools.CatalogResolver;
+import org.apache.xml.serializer.Serializer;
+import org.w3c.dom.Document;
+import org.wyona.security.core.api.Identity;
+import org.wyona.yanel.core.Constants;
+import org.wyona.yanel.core.attributes.viewable.View;
+import org.wyona.yanel.core.serialization.SerializerFactory;
+import org.wyona.yanel.core.source.SourceResolver;
+import org.wyona.yanel.core.transformation.I18nTransformer2;
+import org.wyona.yanel.core.transformation.XIncludeTransformer;
+import org.wyona.yanel.core.util.PathUtil;
+import org.wyona.yanel.impl.resources.ViewDescriptorUsingTemplate.TemplateOption;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import org.apache.log4j.Logger;
+
+/**
+ * This resource type takes care only of the custom configuration.
+ * It behaves much like org.wyona.yanel.impl.resources.usecase.UsecaseResource.
+ * In the resource configuration one can specify views and there a Jelly template and arbitrary XSLTs.
+ * <p>
+ * The templates get the following parameters(subclasses may enrich this set):
+ * <ul>
+ * <li> <b>All the parameters from the incoming request</b>
+ * <li>yanel.path.name
+ * <li>yanel.path
+ * <li>yanel.back2realm
+ * <li>yanel.back2context
+ * <li>yanel.global.htdocs
+ * <li>yanel.resource.htdocs
+ * <li>yanel.requested.language
+ * <li>yanel.content.language
+ * <li>client (i.e. user agent)
+ * <li>yanel.username
+ * <li>yanel.toolbar.status
+ * <li>yanel.reserved.prefix
+ * </ul>
+ * <p>
+ * The view description looks as follows:
+ * <pre>
+ * [yanel:custom-config]
+ *   [views xmlns="http://www.wyona.org/yanel/rti/1.0"]
+ *     [view id="..."]
+ *       [template type="JELLY"]...[/template]
+ *       [xslt]...[/xslt]*
+ *       [mime-type]...[/mime-type]? (e.g. text/html)
+ *       [serializer key="..."/]?
+ *     [/view]
+ *     [screen/]*
+ *     ...
+ *     [fragment/]*
+ * [/yanel:custom-config]
+ * </pre>
+ * The 'fragment' view hints the view adapters that this is not the user screen but rather a fragment generated by AJAX call or something similar.
+ * The 'screen' view is the synonym of 'view', used to stress that the view is not a fragment
+ * */
+public abstract class JellyControllerAdapter extends ControllerAdapter {
+    
+    private static Logger log = Logger.getLogger(JellyControllerAdapter.class);
+
+    // Jelly can be configured to escape the text that it gets from JAVA
+    // This parameter allows to disable escaping.
+    // Accepted values: on, off, true, false, yes, no
+    public static final String ESCAPE_TEXT_IN_JELLY = "escape.text.in.jelly";
+    
+    private HashMap<String, ViewDescriptorUsingTemplate> viewDescriptors;
+    
+    public JellyControllerAdapter() {
+        super();
+    }
+    
+    /**
+     * Checks if the parameter that configures the escaping was passed:
+     * <ul>
+     * <li> As a parameter in the request
+     * <li> As a property in the configuration
+     * </ul>
+     * @return <code>true</code> by default, otherwise depends on the parameter value
+     * */
+    public boolean isEscapeTextEnabled(){
+        String escape = getParameterAsString(ESCAPE_TEXT_IN_JELLY);
+        if(escape == null){
+            try {
+                escape = getConfiguration().getProperty(ESCAPE_TEXT_IN_JELLY);
+            } catch (Exception e) {
+                log.info("Could not detect the property "+ESCAPE_TEXT_IN_JELLY+", will use default value");
+            }
+        }
+        
+        if(escape == null){
+            return true;
+        }
+		if("on".equals(escape.trim().toLowerCase()) || "yes".equals(escape.trim().toLowerCase()) || Boolean.valueOf(escape.trim())){
+		    return true;
+		}
+		return false;
+    }
+    
+    /**
+     *
+     */
+    public ViewDescriptorUsingTemplate[] getViewDescriptors() {
+        if (this.viewDescriptors == null) {
+            try {
+                this.viewDescriptors = new HashMap<String, ViewDescriptorUsingTemplate>();
+                
+                // reads views from configuration:
+                if (getConfiguration() != null && getConfiguration().getCustomConfiguration() != null) {
+                    Document customConfigDoc = getConfiguration().getCustomConfiguration();
+                    Configuration config = ConfigurationUtil.toConfiguration(customConfigDoc.getDocumentElement());
+                    Configuration viewsConfig = config.getChild("views");
+
+                    {
+                        ViewDescriptorUsingTemplate defaultView = null;
+                        
+                        // Collect view definitions
+                        Configuration[] viewConfigs = viewsConfig.getChildren("view");
+                        for (int i = 0; i < viewConfigs.length; i++) {
+                            String id = normalize(viewConfigs[i].getAttribute("id"));
+                            
+                            ViewDescriptorUsingTemplate viewDescriptor = new ViewDescriptorUsingTemplate(id, viewConfigs[i]);
+                            
+                            if(Constants.Request.DEFAULT_VIEW_ID.equals(id)){
+                                defaultView = viewDescriptor;
+                            }
+                            
+                            this.viewDescriptors.put(id, viewDescriptor);
+                        }
+                        
+                        if(defaultView == null){
+                            defaultView = new ViewDescriptorUsingTemplate(Constants.Request.DEFAULT_VIEW_ID);
+                            defaultView.setMimeType("application/xml");
+                            this.viewDescriptors.put(Constants.Request.DEFAULT_VIEW_ID, defaultView);
+                        }
+                    }
+                    
+                    {
+                        // Collect fragment definitions
+                        //log.debug("Get all fragments ...");
+                        Configuration[] fragmentConfigs = viewsConfig.getChildren("fragment");
+                        for (int i = 0; i < fragmentConfigs.length; i++) {
+                            String id = normalize(fragmentConfigs[i].getAttribute("id"));
+                            if(log.isDebugEnabled()) log.debug("Fragment ID: " + id);
+                            
+                            ViewDescriptorUsingTemplate viewDescriptor = new ViewDescriptorUsingTemplate(id, fragmentConfigs[i], true);
+                            if(this.viewDescriptors.keySet().contains(id)){
+                                log.warn("The view with ID '" + id + "' will be ignored as the descriptor with such an ID already exists!");
+                            }else{
+                                this.viewDescriptors.put(id, viewDescriptor);
+                            }
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Views are not properly configured");
+            }
+        }
+        
+        return this.viewDescriptors.values().toArray(new ViewDescriptorUsingTemplate[this.viewDescriptors.size()]);
+    }
+    
+    /**
+     * Runs the pipeline of templates. You can feed an XML, JELLY or XSLT to the pipe.
+     * Every template will be processed and passed through internationalization and XInclude transformers.
+     * So every subsequent template can react to the expanded(resolved) input.
+     */
+    public View getView(String viewId) throws Exception {
+        View view = new View();
+        
+        viewId = normalize(viewId);
+        
+        ViewDescriptorUsingTemplate viewDescriptor = (ViewDescriptorUsingTemplate)getViewDescriptor(viewId);
+        view.setMimeType(viewDescriptor.getMimeType());
+        
+        try {
+            Serializer serializer = viewDescriptor.getSerializer();
+            Serializer xmlSerializer = SerializerFactory.getSerializer(SerializerFactory.XML);
+            
+            InputStream content = null;
+            
+            TemplateOption [] templates = viewDescriptor.getTemplates();
+            //TODO: don't serialize on each template, rather pipe content handlers
+            for (int i = 0; i < templates.length; i++) {
+                // The first one is JELLY or XML, then every other is XSLT
+                if(i + 1 == templates.length){
+                    // Use configured serializer for the last template only
+                    content = processTemplate(content, templates[i], serializer);
+                }else{
+                    // In the intermediate steps only XML is produced
+                    content = processTemplate(content, templates[i], xmlSerializer);
+                }
+            }
+            
+            // write result into view:
+            view.setInputStream(content);
+        } catch(Exception e) {
+            log.error(e + " (" + getPath() + ", " + getRealm() + ")", e);
+            throw new Exception(e);
+        }
+        
+        return view;
+    }
+    
+    /**
+     * @param content Template may be applied to it, e.g. for transformations
+     * @param template Template
+     * @param serializer Serializer
+     * 
+     * @return result of the processing as a stream
+     */
+    protected InputStream processTemplate(InputStream content, TemplateOption template, Serializer serializer) throws Exception{
+        InputStream result = null;
+        
+        SourceResolver uriResolver = new SourceResolver(this);
+        CatalogResolver catalogResolver = new CatalogResolver();
+        
+        InputStream templateInputStream = getTemplateAsStream(template);
+        
+        if(TemplateOption.TYPE_XSLT.equals(template.getType())){
+            // Here after the required template it is 
+            
+            // create xslt transformer:
+            SAXTransformerFactory tf = (SAXTransformerFactory)TransformerFactory.newInstance();
+            tf.setURIResolver(uriResolver);
+            
+            // attach handler:
+            TransformerHandler th = tf.newTransformerHandler(new StreamSource(templateInputStream));
+            
+            th.getTransformer().setURIResolver(uriResolver);
+            Transformer transformer = th.getTransformer();
+            
+            passParameters(transformer);
+            
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            serializer.setOutputStream(baos);
+            th.setResult(new SAXResult(serializer.asContentHandler()));
+
+            // create reader:
+            XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+            xmlReader.setEntityResolver(catalogResolver);
+            xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+            xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", th);
+            xmlReader.setContentHandler(th);
+            
+            // execute:
+            xmlReader.parse(new InputSource(content));
+            
+            result = new ByteArrayInputStream(baos.toByteArray());
+        }else if(TemplateOption.TYPE_JELLY.equals(template.getType())){
+            log.debug("Template type: " + template.getType());
+            JellyContext jellyContext = new JellyContext();
+            passParameters(jellyContext);
+
+            ByteArrayOutputStream jellyResultStream = new ByteArrayOutputStream();
+            
+            XMLOutput jellyOutput = XMLOutput.createXMLOutput(jellyResultStream, isEscapeTextEnabled());
+            jellyContext.runScript(new InputSource(templateInputStream), jellyOutput);
+            jellyOutput.flush();
+            result = new ByteArrayInputStream(jellyResultStream.toByteArray());
+        }else{
+            result = templateInputStream;
+        }
+        
+        //
+        // Pass the result through i18n and XInclude transformers
+        //
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutputStream(baos);
+        
+        // create i18n transformer:
+        I18nTransformer2 i18nTransformer = new I18nTransformer2(getI18NCatalogueNames(), getRequestedLanguage(), getRealm().getDefaultLanguage());
+        i18nTransformer.setEntityResolver(catalogResolver);
+
+
+        // create xinclude transformer:
+        XIncludeTransformer xIncludeTransformer = new XIncludeTransformer();
+        xIncludeTransformer.setResolver(uriResolver);
+        
+        // chain everything together (create a pipeline):
+        xIncludeTransformer.setResult(new SAXResult(i18nTransformer));
+        i18nTransformer.setResult(new SAXResult(serializer.asContentHandler()));
+        
+        // create reader and execute
+        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+        xmlReader.setEntityResolver(catalogResolver);
+        xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+        
+        xmlReader.setContentHandler(new SAXResult(xIncludeTransformer).getHandler());
+        xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", xIncludeTransformer);
+        
+        xmlReader.setContentHandler(new SAXResult(i18nTransformer).getHandler());
+        xmlReader.parse(new InputSource(result));
+        
+        result = new ByteArrayInputStream(baos.toByteArray());
+        
+        return result;
+    }
+    
+    /**
+     * Tries to get the template as input stream. The template can be either accessible through 
+     * a URL or simply from the repository. 
+     *
+     * @param template TemplateOption containing type and URL
+     */
+    protected InputStream getTemplateAsStream(TemplateOption template)throws Exception{
+        log.debug("Template: " + template.getUrl());
+        InputStream templateInputStream = null;
+        if(!template.getUrl().startsWith("/")){
+            // Resolve the URI into the InputStream
+            Source src = new SourceResolver(this).resolve(template.getUrl(), null);
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            TransformerFactory.newInstance().newTransformer().transform(src, new StreamResult(bos));
+            templateInputStream = new ByteArrayInputStream(bos.toByteArray());
+        }else{
+            // Just take the template from the repository node
+            templateInputStream = getRealm().getRepository().getNode(template.getUrl()).getInputStream();
+        }
+        return templateInputStream;
+    }
+    
+    /**
+     * Pass parameters to Jelly context (Add more parameters in subclasses if needed)
+     */
+    protected void passParameters(JellyContext jellyContext) throws Exception{
+        // Attach all parameters that came with the request, such that templates can make use of them.
+        // NOTE: all parameter values will be of type String. In XSLT: <param name="p" value="'actual_value'"/>
+        for (Iterator i = getParameters().entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            jellyContext.setVariable(String.valueOf(entry.getKey()), entry.getValue());
+        }
+        
+        // Set general parameters
+        jellyContext.setVariable("resource", this); // TODO: Somehow this doesn't work!
+
+        jellyContext.setVariable("continuation.id", getContinuation().getId());
+        jellyContext.setVariable("continuation.id.key", YANEL_CONTINUATION_ID);
+
+        jellyContext.setVariable("yanel.path.name", org.wyona.commons.io.PathUtil.getName(getPath()));
+        jellyContext.setVariable("yanel.path", getPath());
+        
+        jellyContext.setVariable("yanel.back2realm", PathUtil.backToRealm(getPath()));
+        jellyContext.setVariable("yanel.back2context", PathUtil.backToContext(realm, getPath()));
+        
+        jellyContext.setVariable("yanel.global.htdocs", PathUtil.getGlobalHtdocsPath(this));
+        jellyContext.setVariable("yanel.resource.htdocs", PathUtil.getResourcesHtdocsPath(this));
+        
+        jellyContext.setVariable("yanel.requested.language", getRequestedLanguage());
+        jellyContext.setVariable("yanel.content.language", getContentLanguage());
+        
+        String userAgent = getEnvironment().getRequest().getHeader("User-Agent");
+        String client = getClient(userAgent);
+        if (client != null) jellyContext.setVariable("client", client);
+
+        // username
+        String username = getUsername();
+        if (username != null) jellyContext.setVariable("yanel.username", username);
+
+        // Add toolbar status
+        String toolbarStatus = getToolbarStatus();
+        if (toolbarStatus != null) jellyContext.setVariable("yanel.toolbar.status", toolbarStatus);
+        
+        jellyContext.setVariable("yanel.reserved.prefix", getYanel().getReservedPrefix());
+    }
+    
+    /**
+     * Add more parameters in subclasses if needed
+     * */
+    protected void passParameters(Transformer transformer) throws Exception{
+        // Attach all parameters that came with the request. Templates can make use of them.
+        // NOTE: all parameter values will be of type String. In XSLT: <param name="p" value="'actual_value'"/>
+        for (Iterator i = getParameters().entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            if (entry.getValue() instanceof String) {
+                String value = (String) entry.getValue();
+                transformer.setParameter(String.valueOf(entry.getKey()), value);
+            } else if(entry.getValue() instanceof String[]){
+                // values separated by a space
+                String separator = " ";
+                
+                StringBuffer finalValue = new StringBuffer();
+                String [] values = (String[]) entry.getValue();
+                for (int j = 0; j < values.length; j++) {
+                    finalValue.append(values[j]);
+                    if(j + 1 != values.length){
+                        finalValue.append(separator);
+                    }
+                }
+                transformer.setParameter(String.valueOf(entry.getKey()), finalValue);
+            } else{
+                // Never happens
+            }
+        }
+        
+        // Set general parameters
+        transformer.setParameter("yanel.path.name", org.wyona.commons.io.PathUtil.getName(getPath()));
+        transformer.setParameter("yanel.path", getPath());
+        
+        transformer.setParameter("yanel.back2realm", PathUtil.backToRealm(getPath()));
+        transformer.setParameter("yanel.back2context", PathUtil.backToContext(realm, getPath()));
+        
+        transformer.setParameter("yanel.global.htdocs", PathUtil.getGlobalHtdocsPath(this));
+        transformer.setParameter("yanel.resource.htdocs", PathUtil.getResourcesHtdocsPath(this));
+        
+        transformer.setParameter("yanel.requested.language", getRequestedLanguage());
+        transformer.setParameter("yanel.content.language", getContentLanguage());
+        
+        String userAgent = getEnvironment().getRequest().getHeader("User-Agent");
+        String client = getClient(userAgent);
+        if (client != null) transformer.setParameter("client", client);
+
+        // username
+        String username = getUsername();
+        if (username != null) transformer.setParameter("yanel.username", username);
+
+        // Add toolbar status
+        String toolbarStatus = getToolbarStatus();
+        if (toolbarStatus != null) transformer.setParameter("yanel.toolbar.status", toolbarStatus);
+        
+        transformer.setParameter("yanel.reserved.prefix", getYanel().getReservedPrefix());
+    }
+    
+    protected final String getUsername() {
+        Identity identity = getEnvironment().getIdentity();
+        if (identity != null) return identity.getUsername();
+        return null;
+    }
+    
+    protected final String getClient(String userAgent) {
+        if (userAgent.indexOf("Firefox") > 0) {
+            return "firefox";
+        } else if (userAgent.indexOf("MSIE") > 0) {
+            return "msie";
+        } else {
+            log.warn("Client could not be recognized: " + userAgent);
+            return null;
+        }
+    }
+    
+    protected final String getToolbarStatus() {
+        // TODO: Use YanelServlet.TOOLBAR_KEY instead "toolbar"!
+        return (String) getEnvironment().getRequest().getSession(true).getAttribute("toolbar");
+    }
+    
+}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyConversationAdapter.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/JellyConversationAdapter.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyConversationAdapter.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/JellyConversationAdapter.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,139 @@
+package org.wyona.yanel.impl.resources;
+
+import javax.xml.transform.Transformer;
+
+import org.apache.commons.jelly.JellyContext;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Deals with conversations.
+ * A conversation is mapped to a path.
+ * */
+//TODO: is path ok as a key of the conversation state
+//TODO[high]: conversation state object must be synchronized, because it is put into the 
+//session and may be accessed by different browser instances
+abstract class JellyConversationAdapter extends JellyControllerAdapter {
+
+    private static Logger log = Logger.getLogger(JellyConversationAdapter.class);
+
+    /**
+     * This is the view where the user should get after all the activities are finished.
+     * */
+    protected static final String DONE_VIEW_ID = "done";
+    
+    /**
+     * This is the view where the user should get when the conversation is canceled.
+     * */
+    protected static final String CANCEL_VIEW_ID = "cancel";
+    
+    /**
+     * This parameter is useful when the referrer url is not enough.
+     * For example, the page may needs to go to some page (other than a referrer) after the conversation has been completed.  
+     * */
+    public static final String PARAM_GOTO_URL = "goto.url";
+    
+    private ConversationState conversationState;
+    
+    public JellyConversationAdapter() {
+        super();
+        supportedViewIds.add(CANCEL_VIEW_ID);
+        supportedViewIds.add(DONE_VIEW_ID);
+    }
+    
+    /**
+     * Initialize or keep up to date the conversation state
+     */
+    protected abstract void init() throws Exception;
+    
+    /**
+     * In this method the conversation should be commited. Data may need to be validated befor commit.
+     */
+    public abstract void commit()throws Exception;
+    
+    
+    /**
+     * Returns the conversation state object. It may be available in the session
+     * or invalidated (no more in the session).
+     * @return - <code>null</code> when called before initConversation(), in other cases the conversation state object (maybe invalidated already)
+     * */
+    protected final ConversationState getConversationState(){
+        if(conversationState == null){
+            conversationState = (ConversationState)getEnvironment().getRequest().getSession(true).getAttribute(getPath());
+        }
+        return conversationState;
+    }
+    
+    /**
+     * Checks if the conversation is already in the session and creates it if necessary
+     * */
+    protected final ConversationState initConversation(Object model, Usecase usecase, String resourcePath){
+        log.warn("Continuation: " + getContinuation().getId());
+
+        ConversationState cs = new ConversationState(model);
+        cs.setUsecase(usecase);
+        cs.setResourcePath(resourcePath);
+        
+        ConversationState current = getConversationState();
+        if (current != null && !current.isInvalidated()) {
+            // Refresh the properties, because they are changing during the conversation
+            current.setModel(cs.getModel());
+            current.setCurrentScreen(cs.getCurrentScreen());
+        } else {
+            cs.setRefererUrl(getEnvironment().getRequest().getHeader("Referer"));
+            // The new conversation state becomes current
+            current = cs;
+        }
+        
+        String gotoUrl = getParameterAsString(PARAM_GOTO_URL);
+        if(gotoUrl != null){
+            current.setGotoUrl(gotoUrl);
+        }
+        
+        log.warn("Attach conversation state for usecase '" + current.getUsecase() + "' to session with id '" + getPath() + "'");
+        getEnvironment().getRequest().getSession(true).setAttribute(getPath(), current);
+        return current;
+    }
+    
+    /**
+     * Removes the conversation state from the session and invalidates the conversation state object.
+     * */
+    protected final void destroyConversation(){
+        ConversationState current = getConversationState();
+        if(current != null){
+            current.invalidate();
+        }
+        getEnvironment().getRequest().getSession(true).removeAttribute(getPath());
+    }
+    
+    /**
+     * No additional parameters passed. Subclasses should consult conversation state for additional parameters
+     */
+    protected void passParameters(JellyContext jellyContext) throws Exception {
+        super.passParameters(jellyContext);
+        ConversationState cs = getConversationState();
+        if (cs != null) {
+            if(cs.getGotoUrl() != null){
+                jellyContext.setVariable(PARAM_GOTO_URL, cs.getGotoUrl());
+            }
+        } else {
+            log.warn("The conversation was not initialized");
+        }
+        
+    }
+    
+    /**
+     * No additional parameters passed. Subclasses should consult conversation state for additional parameters
+     */
+    protected void passParameters(Transformer transformer) throws Exception {
+        super.passParameters(transformer);
+        ConversationState cs = getConversationState();
+        if (cs != null) {
+            if(cs.getGotoUrl() != null){
+                transformer.setParameter(PARAM_GOTO_URL, cs.getGotoUrl());
+            }
+        } else {
+            log.warn("The conversation was not initialized");
+        }
+    }
+}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ResourceAdapter.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ResourceAdapter.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ResourceAdapter.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ResourceAdapter.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,51 @@
+package org.wyona.yanel.impl.resources;
+
+
+/**
+ * The adapter knows how to adapt resources. It works as a facade
+ * for adapted resource that are implementing some specific functionality. 
+ * E.g. a resource can be creatable, deletable, modifiable...
+ * <p>
+ * Normally the adapter should be instantiated by Yanel servlet    
+ * */
+public interface ResourceAdapter{    
+    public static final String PARAM_ADAPTED_RESOURCE_PATH = "adapted.resource.path";
+    
+    public static enum Usecase{
+        /**
+         * If this usecase is specified, then a resource of the specified type will create something.
+         */
+        create,
+        /**
+         * If this usecase is specified, then a resource of the specified type will modify something.
+         */
+        modify,
+        /**
+         * If this usecase is specified, then a resource of the specified type will delete something.
+         */
+        //TODO: the value must be "delete", but YanelServlet does not allow that?
+        remove;
+        
+        /**
+         * Create the usecase out of the given parameter ignoring the case of the string.
+         * Behaves much like simple valueOf
+         * @return null when the parameter is null, otherwise tries to create the usecase.
+         * */
+        public static Usecase caseInsensitiveValueOf(String usecase){
+            if(usecase == null){
+                return null;
+            }
+            
+            return Usecase.valueOf(usecase.toLowerCase());
+        }
+    }
+    
+    public String getAdaptedResourcePath();
+    public void setAdaptedResourcePath(String adaptedResourcePath);
+    
+    /**
+     * The adapter knows how to adapt the resource for specific usecases
+     * */
+    public Usecase getUsecase();
+    public void setUsecase(Usecase usecase);
+}

Copied: public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ViewDescriptorUsingTemplate.java (from rev 47361, public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/ViewDescriptorUsingTemplate.java)
===================================================================
--- public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ViewDescriptorUsingTemplate.java	                        (rev 0)
+++ public/yanel/contributions/resources/creatable-modifiable-deletable-v3/src/java/org/wyona/yanel/impl/resources/jellyadapterofcmdv3/ViewDescriptorUsingTemplate.java	2010-01-30 14:57:02 UTC (rev 47362)
@@ -0,0 +1,195 @@
+package org.wyona.yanel.impl.resources;
+
+import java.util.Enumeration;
+import java.util.Properties;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.xml.serializer.Serializer;
+import org.wyona.commons.io.MimeTypeUtil;
+import org.wyona.yanel.core.attributes.viewable.ViewDescriptor;
+import org.wyona.yanel.core.serialization.SerializerFactory;
+
+/**
+ * A view is configured with a template and a set of XSLTs:
+ * <pre>
+ * [view id="x"]
+ *  [template type="JELLY"]...location...[/template]
+ *  [xslt]_location_[/xslt]
+ *  ...
+ *  [xslt]_location_[/xslt]
+ *  [mime-type]...(test/html)...[mime-type]
+ *  [serializer key="...(XHTML_STRICT)..."]
+ *  ... serializer props...
+ *  [/serializer]
+ *  
+ * [/view]
+ * </pred>
+ * */
+public class ViewDescriptorUsingTemplate extends ViewDescriptor {
+
+    /**
+     *
+     */
+    public static class TemplateOption{
+        public static final String TYPE_JELLY = "JELLY";
+        public static final String TYPE_XSLT = "XSLT";
+        /**
+         * This is to say that it is raw XML
+         */
+        public static final String TYPE_XML = "XML";
+        
+        private String type;
+        private String url;
+        
+        public TemplateOption(String type, String url) {
+            this.type = type;
+            this.url = url;
+        }
+        
+        public String getType() {
+            return type;
+        }
+        
+        public String getUrl() {
+            return url;
+        }
+    }
+    
+    private boolean fragment = false;
+    
+    protected TemplateOption [] templates = new TemplateOption[0];
+    
+    protected String serializerKey = SerializerFactory.XHTML_STRICT_KEY;
+    protected Properties serializerProperties = new Properties();
+
+    protected ViewDescriptorUsingTemplate(String id){
+        super(id);
+    }
+    
+    public ViewDescriptorUsingTemplate(String id, Configuration viewDefinition) throws ConfigurationException{
+        this(id, viewDefinition, false);
+    }
+    
+    public ViewDescriptorUsingTemplate(String id, Configuration viewDefinition, boolean isFragment) throws ConfigurationException{
+        super(id);
+        configure(viewDefinition);
+        this.fragment = isFragment;
+    }
+    
+    /**
+     * Defaults:
+     * <p>
+     * mime-type : text/html<br>
+     * serializer: XHTML_STRICT<br>
+     * serializer props: empty
+     */
+    protected void configure(Configuration config) throws ConfigurationException {
+        Configuration[] templateConfigs = config.getChildren("template");
+        if(templateConfigs.length == 0){
+            throw new ConfigurationException("A template is not specified");
+        }
+        
+        Configuration[] xsltConfigs = config.getChildren("xslt");
+        
+        templates = new TemplateOption[1 + xsltConfigs.length];
+        
+        templates[0] = new TemplateOption(templateConfigs[0].getAttribute("type"), templateConfigs[0].getValue());
+        for (int i = 0; i < xsltConfigs.length; i++) {
+            templates[i+1] = new TemplateOption(TemplateOption.TYPE_XSLT, xsltConfigs[i].getValue());
+        }
+        
+        Configuration mimeTypeConfig = config.getChild("mime-type", false);
+        if (mimeTypeConfig != null) {
+            setMimeType(mimeTypeConfig.getValue());
+        }else{
+            setMimeType("text/html");
+        }
+        
+        Configuration serializerConfig = config.getChild("serializer", false);
+        if (serializerConfig != null) {
+            serializerKey = (serializerConfig.getAttribute("key") == null ? serializerKey : serializerConfig.getAttribute("key"));
+            serializerProperties = new Properties();
+            Configuration propertyConfig = serializerConfig.getChild("omit-xml-declaration", false);
+            if (propertyConfig != null) {
+                serializerProperties.setProperty("omit-xml-declaration", propertyConfig.getValue());
+            }
+            propertyConfig = serializerConfig.getChild("doctype-public", false);
+            if (propertyConfig != null) {
+                serializerProperties.setProperty("doctype-public", propertyConfig.getValue());
+            }
+            propertyConfig = serializerConfig.getChild("doctype-system", false);
+            if (propertyConfig != null) {
+                serializerProperties.setProperty("doctype-sytem", propertyConfig.getValue());
+            }
+        }
+    }
+
+    public String getSerializerKey() {
+        return serializerKey;
+    }
+
+    public void setSerializerKey(String serializerKey) {
+        this.serializerKey = serializerKey;
+    }
+
+    public Properties getSerializerProperties() {
+        return serializerProperties;
+    }
+
+    public void setSerializerProperties(Properties serializerProperties) {
+        this.serializerProperties = serializerProperties;
+    }
+
+    public TemplateOption[] getTemplates() {
+        return templates;
+    }
+    
+    /**
+     * @return <code>true</code> when the view represents a fragment, not a screen.
+     * For instance, a fragment view is useful when doing AJAX call
+     * */
+    public boolean isFragment(){
+        return fragment;
+    }
+    
+    /**
+     * Creates an html or xml serializer for this view descriptor
+     */
+    public Serializer getSerializer() throws Exception {
+        Serializer serializer = null;
+        String serializerKey = getSerializerKey();
+        if (serializerKey != null) {
+            serializer = SerializerFactory.getSerializer(serializerKey);
+            if (serializer == null) {
+                throw new Exception("could not create serializer for key: " + serializerKey);
+            }
+        } else {
+            String mimeType = getMimeType();
+
+            if (MimeTypeUtil.isHTML(mimeType) && !MimeTypeUtil.isXML(mimeType)) {
+                serializer = SerializerFactory.getSerializer(SerializerFactory.HTML_TRANSITIONAL);
+            } else if (MimeTypeUtil.isHTML(mimeType) && MimeTypeUtil.isXML(mimeType)){
+                serializer = SerializerFactory.getSerializer(SerializerFactory.XHTML_STRICT);
+            } else if (MimeTypeUtil.isXML(mimeType)) {
+                serializer = SerializerFactory.getSerializer(SerializerFactory.XML);
+            } else if (MimeTypeUtil.isTextual(mimeType)) {
+                serializer = SerializerFactory.getSerializer(SerializerFactory.TEXT);
+            } else{
+                serializer = SerializerFactory.getSerializer(SerializerFactory.XHTML_STRICT);
+            }
+        }
+        // allow to override xml declaration and doctype:
+        Properties properties = getSerializerProperties();
+        if (properties != null) {
+            Enumeration propNames = properties.propertyNames();
+            while (propNames.hasMoreElements()) {
+                String name = (String)propNames.nextElement();
+                String value = properties.getProperty(name);
+
+                serializer.getOutputFormat().setProperty(name, value);
+            }
+        }
+        return serializer;
+    }
+}



More information about the Yanel-commits mailing list