org.yawlfoundation.yawl.scheduling.FormGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.yawlfoundation.yawl.scheduling.FormGenerator.java

Source

/*
 * Copyright (c) 2004-2012 The YAWL Foundation. All rights reserved.
 * The YAWL Foundation is a collaboration of individuals and
 * organisations who are committed to improving workflow technology.
 *
 * This file is part of YAWL. YAWL is free software: you can
 * redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation.
 *
 * YAWL is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with YAWL. If not, see <http://www.gnu.org/licenses/>.
 */

package org.yawlfoundation.yawl.scheduling;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.yawlfoundation.yawl.resourcing.resource.Participant;
import org.yawlfoundation.yawl.resourcing.rsInterface.ResourceGatewayException;
import org.yawlfoundation.yawl.scheduling.resource.ResourceServiceInterface;
import org.yawlfoundation.yawl.scheduling.util.PropertyReader;
import org.yawlfoundation.yawl.scheduling.util.Utils;
import org.yawlfoundation.yawl.scheduling.util.XMLUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.datatype.DatatypeConfigurationException;
import java.io.IOException;
import java.text.ParseException;
import java.util.*;

/**
 * shows custom form for configuring utilisation plan
 *
 * @author tbe
 * @version $Id: FormGenerator.java 30323 2011-05-17 10:50:07Z tbe $
 */
public class FormGenerator implements Constants {

    private static Logger _log = LogManager.getLogger(FormGenerator.class);

    private HttpServletRequest request;
    private HttpServletResponse response;
    private HttpSession session;
    private StringBuilder buffer = new StringBuilder(), bufferTop = new StringBuilder(),
            bufferBottom = new StringBuilder(), bufferBottomDebug = new StringBuilder();
    private SchedulingService ss;
    private Scheduler scheduler;
    private ResourceServiceInterface rs;
    private ConfigManager config;
    private PropertyReader _props;
    private String source;
    private Element wirElement;
    private String userName;
    private List<Element> activities;

    // used for adding new reservations/relations
    int count = 0;

    // show short form if used only for data input without reservation request to RS
    boolean isShortForm = false;
    boolean canConfirmReservations = false;
    boolean reschedulingRUPAfterEditInCustomForm;
    boolean wrapBeforeResource = true;

    public FormGenerator(HttpServletRequest request, HttpServletResponse response) {
        try {
            config = ConfigManager.getFromRequest(request);
            ss = SchedulingService.getInstance();
            scheduler = new Scheduler();
            rs = ResourceServiceInterface.getInstance();
            _props = PropertyReader.getInstance();
            this.request = request;
            this.response = response;
            session = request.getSession();
            reschedulingRUPAfterEditInCustomForm = _props.getBooleanProperty(PropertyReader.SCHEDULING,
                    "reschedulingRUPAfterEditInCustomForm");

        } catch (Throwable e) {
            handleError(e);
        }
    }

    public ConfigManager getConfig() {
        return config;
    }

    public String outForm() {
        try {
            outFormPriv();

            buffer.insert(0, "\r\n\r\n<script type=\"text/javascript\">\r\n" + bufferTop + "</script>");

            buffer.append("\r\n\r\n<script type=\"text/javascript\">");
            buffer.append("window.onload=function(){\r\n" + bufferBottom + "}");
            buffer.append("</script>");

            buffer.append("\r\n\r\n<!--\r\n" + bufferBottomDebug + "\r\n-->");

            return buffer.toString();
        } catch (Throwable e) {
            //_log.error("cannot create form", e);
            handleError(e);
            return null;
        }
    }

    private void outFormPriv() throws Exception {
        String reschedulingKey = request.getParameter("reschedulingKey");
        source = request.getParameter("source");
        String itemid = request.getParameter("workitem");
        String handle = request.getParameter("handle");
        String participantid = request.getParameter("participantid");

        try {
            Participant participant = rs.getParticipant(participantid);
            userName = participant.getFullName();
        } catch (Exception e) {
            _log.error("Cannot get username for participant id: " + participantid, e);
        }

        if (!rs.isValidUserSession(handle)) {
            _log.warn("User handle is invalid: " + handle + ", redirecting to start page...");
            redirect(source.substring(0, source.lastIndexOf("/")));
        }

        wirElement = Utils.string2Element(rs.getWorkItem(itemid, handle));

        String caseId = Utils.extractCaseId(wirElement.getChildText("caseid"));
        String taskId = wirElement.getChildText("taskid");
        String taskName = taskId.substring(0, taskId.lastIndexOf("_"));

        List<Element> children = wirElement.getChild("updateddata").getChildren();
        Element task = (children.isEmpty() ? null : children.get(0));
        if (task == null) {
            children = wirElement.getChild("data").getChildren();
            task = (children.isEmpty() ? null : children.get(0));
        }

        boolean complete = request.getParameter("Complete") != null;
        boolean completeForce = request.getParameter("CompleteForce") != null
                && !request.getParameter("CompleteForce").isEmpty();
        boolean save = reschedulingKey != null || request.getParameter("Save") != null;
        boolean confirmReservations = request.getParameter("ConfirmReservations") != null;
        boolean cancel = request.getParameter("Cancel") != null;
        boolean language = request.getParameter(LANGUAGE_ATTRIBUTE_NAME) != null
                && !request.getParameter(LANGUAGE_ATTRIBUTE_NAME).isEmpty();
        isShortForm = Boolean.parseBoolean(task.getChildText("shortForm"));
        canConfirmReservations = Boolean.parseBoolean(task.getChildText("canConfirmReservations"));
        _log.debug("cancel,save,confirmReservations,complete,language,isShortForm,canConfirmReservations=" + cancel
                + "," + save + "," + confirmReservations + "," + complete + "," + language + "," + isShortForm + ","
                + canConfirmReservations);

        if (cancel) {
            redirect();
        } else if (completeForce) {
            completeWorkitem(taskName, itemid, handle);
        } else if (complete || save || language || confirmReservations) {
            Case cas = ss.loadCase(caseId);
            Document rup = cas.getRUP();
            Document rupOriginal = new Document(
                    Utils.string2Element(Utils.element2String(rup.getRootElement(), false)));
            String msgDB = "customForm";
            activities = XMLUtils.getXMLObjects(rup, XMLUtils.getXPATH_Activities());

            updateRUPFromRequest(rup);

            // set times of rup if rescheduling required
            if (reschedulingRUPAfterEditInCustomForm && reschedulingKey != null && !reschedulingKey.isEmpty()) {
                String activityName = reschedulingKey.substring((XML_ACTIVITY + "_").length());
                activityName = activityName.substring(0, activityName.indexOf("_"));
                List<Element> activities = XMLUtils.getXMLObjects(rup, XMLUtils.getXPATH_Activities(activityName));
                if (!activities.isEmpty()) {
                    scheduler.setTimes(rup, activities.get(0), !isShortForm, false, null);
                }
            }

            // reschedule rup at complete of short custom form
            if (isShortForm && (complete || save || confirmReservations)) {
                scheduler.findTimeSlot(rup, !isShortForm);
            }

            // full xsd validation only, if explicit datatype validation has not found any errors
            if (!XMLUtils.hasErrors(rup.getRootElement())) {
                validateXSD(rup);
            }

            if (confirmReservations) {
                String xpath = XMLUtils.getXPATH_Activities();
                List<Element> activities = XMLUtils.getXMLObjects(rup, xpath);
                for (Element activity : activities) {
                    List<Element> reservations = activity.getChildren(XML_RESERVATION);
                    for (Element reservation : reservations) {
                        if (RESOURCE_STATUS_REQUESTED.equals(reservation.getChildText(XML_STATUSTOBE))) {
                            reservation.getChild(XML_STATUSTOBE).setText(RESOURCE_STATUS_RESERVED);
                        }
                    }
                }
            }

            try {
                ss.checkRelations(rup);
                rup = rs.saveReservations(rup, !complete && !confirmReservations, true);
            } catch (Exception e) {
                _log.error("Exception", e);
                XMLUtils.addErrorValue(rup.getRootElement(), !isShortForm, "msgRUPSaveRSError", e.getMessage());
            }

            boolean haveTosave = XMLUtils.different(rupOriginal.getRootElement(), rup.getRootElement());
            if (haveTosave && (complete || save || confirmReservations)) {
                try {
                    ss.saveRupToDatabase(caseId, userName, rup, msgDB);
                    haveTosave = false;
                } catch (Exception e) {
                    XMLUtils.addErrorValue(rup.getRootElement(), true, "msgRUPSaveDBError", e.getMessage());
                }
            } else {
                _log.debug("Don't have to save rup");
            }

            if (complete) {
                ss.startMessageTransfers(caseId, rup);
            }

            if (complete && !XMLUtils.hasErrors(rup.getRootElement())) {
                completeWorkitem(taskName, itemid, handle);
            } else {
                activities = XMLUtils.getXMLObjects(rup, XMLUtils.getXPATH_Activities());
                buffer.append(getForm(cas, haveTosave));

                // show javascript alert to confirm the completing of work item
                if (complete) {
                    bufferTop.append(" msgCompleteForce=\"").append(config.getLocalizedString("msgCompleteForce"))
                            .append("\";\r\n");
                    bufferBottom.append(" completeForce();\r\n");
                }
            }
        } else { // first call
            Case cas = ss.loadCase(caseId);
            Document rup = cas.getRUP();
            Document rupOriginal = new Document(
                    Utils.string2Element(Utils.element2String(rup.getRootElement(), false)));
            Set<String> addedActivityNames = new HashSet<String>();

            // extend RUP from model specification
            ss.extendRUPFromYAWLModel(rup, addedActivityNames);

            // extend RUP with variable from YAWL
            addedActivityNames.addAll(ss.getDiffActivityNames(rup.getRootElement(), task.getChild(XML_RUP)));
            if (XMLUtils.mergeElements(rup.getRootElement(), task.getChild(XML_RUP))) {
                _log.info("extend rup for case " + caseId + " from YAWL variable: "
                        + Utils.document2String(rup, false));
            }

            // extend RUP with historical data
            ss.completeRupFromHistory(rup, addedActivityNames);

            boolean haveToSave = XMLUtils.different(rupOriginal.getRootElement(), rup.getRootElement());
            validateAllElements(rup.getRootElement());
            activities = XMLUtils.getXMLObjects(rup, XMLUtils.getXPATH_Activities());
            buffer.append(getForm(cas, haveToSave));
        }
    }

    /**
     * @param taskName
     * @param itemid
     * @param handle
     * @throws IOException
     */
    private void completeWorkitem(String taskName, String itemid, String handle)
            throws IOException, ResourceGatewayException {
        Element taskUpd = new Element(taskName);
        String taskUpdStr = Utils.element2String(taskUpd, false);
        String result = rs.updateWorkItemData(itemid, taskUpdStr, handle);

        _log.debug("result: " + result);
        source += "?complete=true"; // workitem in CUI completen

        redirect();
    }

    /**
     * - remove empty 'dummy' resOrUtils
     * - set RequestType to POU, but only if SOU or EOU is not set
     */
    public void updateActivities(Document doc) throws JDOMException {
        activities = XMLUtils.getXMLObjects(doc, XMLUtils.getXPATH_Activities());
        for (Element activity : activities) {
            String xpath = XMLUtils.getXPATH_ActivityElement(activity.getChildText(XML_ACTIVITYNAME),
                    "*[count(./" + XML_DUMMY + ")>0]", null);

            List<Element> resOrUtils = XMLUtils.getXMLObjects(doc, xpath);
            for (Element resOrUtil : resOrUtils) {
                activity.removeContent(resOrUtil);
            }

            Element requestType = activity.getChild(XML_REQUESTTYPE);
            if (requestType == null) {
                requestType = new Element(XML_REQUESTTYPE);
                activity.addContent(requestType);
            }
            if (requestType.getText().isEmpty()) {
                requestType.setText(UTILISATION_TYPE_PLAN);
            }
        }
    }

    /**
     * get ResourceUtilisationPlan updated with data from request
     *
     * @return
     */
    private void updateRUPFromRequest(Document doc) throws Exception {
        String xpath = XMLUtils.getXPATH_RUP();
        Element rupElement = XMLUtils.getElement(doc, xpath);

        // remove elements from rup for reinsert in next step
        for (Element activity : activities) {
            activity.removeChildren(Constants.XML_RESERVATION);
            activity.removeChildren(Constants.XML_UTILISATIONREL);
            activity.removeChildren(Constants.XML_MSGTRANSFER);
        }

        XMLUtils.removeAttributes(rupElement, XML_ERROR);
        XMLUtils.removeAttributes(rupElement, XML_WARNING);

        // build new parameter list with rup fields only
        Map<String, Object[]> params = new HashMap<String, Object[]>();
        for (String key : new ArrayList<String>(request.getParameterMap().keySet())) {
            if (!key.startsWith(XML_ACTIVITY + "_") || key.contains(XML_RESOURCE_TYPE)
                    || key.endsWith("__sexyComboHidden")) {
                continue;
            }

            Object[] values = ((Object[]) request.getParameterMap().get(key));

            // for sexycombo plugin only: write value of key "bla__sexyCombo" to newKey "bla"
            if (key.endsWith("__sexyCombo")) {
                String newKey = key.substring(0, key.length() - "__sexyCombo".length());
                params.put(newKey, values);
            } else if (!params.containsKey(key)) {
                params.put(key, values);
            }
        }

        ArrayList<String> keys = new ArrayList<String>(params.keySet());
        final List<String> possibleActivities = Utils
                .parseCSV(_props.getSchedulingProperty("possibleActivitiesSorted"));
        Collections.sort(keys, new Comparator<String>() {
            public int compare(String a1, String a2) {
                if (a1.startsWith(XML_ACTIVITY)) {
                    a1 = a1.substring(XML_ACTIVITY.length() + 1);
                    a1 = a1.substring(0, a1.indexOf("_"));
                }
                if (a2.startsWith(XML_ACTIVITY)) {
                    a2 = a2.substring(XML_ACTIVITY.length() + 1);
                    a2 = a2.substring(0, a2.indexOf("_"));
                }
                return possibleActivities.indexOf(a1) - possibleActivities.indexOf(a2);
            }
        });

        // update RUP with input values
        for (String key : keys) {
            Object[] values = params.get(key);
            String value = (String) values[0];

            StringTokenizer st = new StringTokenizer(key, "_");
            st.nextToken(); // ignore 'Activity.'
            String activityName = st.nextToken();
            xpath = XMLUtils.getXPATH_Activities(activityName);
            Element activity = XMLUtils.getElement(doc, xpath);
            if (activity == null) {
                activity = getTemplate(XML_ACTIVITY);
                Element name = activity.getChild(XML_ACTIVITYNAME);
                name.setText(activityName);
                rupElement.addContent(activity);
            }

            Element var = null;
            String resOrUtilName = st.nextToken();
            if (resOrUtilName.equals(XML_RESERVATION) || resOrUtilName.equals(XML_UTILISATIONREL)
                    || resOrUtilName.equals(XML_MSGTRANSFER)) {
                Integer resOrUtilIndex = Integer.valueOf(st.nextToken().substring(1)); // index, remove '#'
                xpath = XMLUtils.getXPATH_ActivityElement(activityName, resOrUtilName, resOrUtilIndex);

                Element resOrUtilElem = XMLUtils.getElement(doc, xpath);

                // insert empty 'dummy' resOrUtils, will be removed later
                while (resOrUtilElem == null) {
                    resOrUtilElem = getTemplate(resOrUtilName);
                    resOrUtilElem.addContent(new Element(XML_DUMMY));
                    activity.addContent(resOrUtilElem);

                    resOrUtilElem = XMLUtils.getElement(doc, xpath);
                }
                resOrUtilElem.removeChild(XML_DUMMY); // only if exists one

                String pathName = "";
                while (st.hasMoreTokens()) {
                    String elementName = st.nextToken();
                    pathName += elementName;
                    xpath = XMLUtils.getXPATH_ResOrUtilElement(activityName, resOrUtilName, resOrUtilIndex,
                            pathName);
                    var = XMLUtils.getElement(doc, xpath);
                    if (var == null) {
                        var = new Element(elementName);
                        resOrUtilElem.addContent(var);
                    }
                    resOrUtilElem = var;
                    pathName += "/";
                }

            } else {
                xpath = XMLUtils.getXPATH_ActivityElement(activityName, resOrUtilName, null);
                var = XMLUtils.getElement(doc, xpath);
                if (var == null) {
                    var = new Element(resOrUtilName);
                    activity.addContent(var);
                }
            }

            String cssClass = validateElement(var, new ArrayList<Element>());
            if (cssClass.equals(CSS_DATEINPUT)) {
                try {
                    Date date = Utils.string2Date(value, isShortForm ? Utils.DATE_PATTERN : Utils.DATETIME_PATTERN);
                    XMLUtils.setDateValue(var, date);
                } catch (ParseException e) {
                    //_log.error("cannot parse into date" + value);
                }
            } else if (cssClass.equals(CSS_DURATIONINPUT)) {
                try {
                    value = Utils.stringMinutes2stringXMLDuration(value);
                } catch (Exception e) {
                    //_log.error("cannot parse into duration" + value);
                }
                XMLUtils.setStringValue(var, value);
            } else {
                XMLUtils.setStringValue(var, value);
            }

            validateElement(var, new ArrayList<Element>());
        }

        updateActivities(doc);
    }

    /**
     * validates element and their children recursively
     *
     * @param element
     * @return
     */
    private void validateAllElements(Element element)
            throws DatatypeConfigurationException, IOException, JDOMException {
        if (element != null) {
            validateElement(element, new ArrayList<Element>());
            for (Element child : (List<Element>) element.getChildren()) {
                validateAllElements(child);
            }
        }
    }

    /**
     * validates element and return css class and restrictions
     *
     * @param element
     * @param restrictions
     * @return
     */
    private String validateElement(Element element, List<Element> restrictions)
            throws DatatypeConfigurationException, IOException, JDOMException {
        String xpath = "//xs:element[@name='" + element.getName() + "']";
        for (Document schemaDoc : XMLUtils.getSchemaDocs()) {
            Element schemaElement = XMLUtils.getElement(schemaDoc, xpath);
            if (schemaElement != null) {
                return XMLUtils.validateElement(element, schemaElement, restrictions, !isShortForm);
            }
        }

        _log.error("cannot find element '" + element.getName() + "' in schema");
        return null;
    }

    /**
     * validates resource utilisation plan against syntax and data types of schema
     *
     * @param rup
     * @return
     */
    private void validateXSD(Document rup) {
        try {
            //validate against schema
            XMLUtils.validate(rup.getRootElement());
        } catch (Exception e) {
            _log.error("cannot validate rup", e);
            String msg = e.getMessage() == null ? "null" : e.getMessage().replaceAll("\"", "&quot;");
            XMLUtils.addErrorValue(rup.getRootElement(), !isShortForm, "msgRUPInvalid", msg);
        }
    }

    /**
     * parses the ResourceUtilisationPlan and
     * 1) generates HTML form for input of ResourceUtilisationPlan
     * 2) generates list with keys for Reservations and UtilisationRelations to check
     *
     * @param cas
     * @param haveTosave
     * @return
     */
    protected StringBuffer getForm(Case cas, boolean haveTosave) throws IOException, JDOMException {
        Document rup = cas.getRUP();
        bufferBottomDebug.append(Utils.element2String(rup.getRootElement(), true));

        StringBuffer buffer = new StringBuffer();

        bufferTop.append(" window.name = \"").append(config.getLocalizedString("titleSchedulingPage"))
                .append("\";\r\n");

        buffer.append("\r\n\r\n<div style=\"display: none\">");
        buffer.append("<table>");
        buffer.append(getRow(getTemplate(XML_RESERVATION), XML_RESERVATION + "Template", false));
        buffer.append(getRow(getTemplate(XML_UTILISATIONREL), XML_UTILISATIONREL + "Template", false));
        buffer.append(getRow(getTemplate(XML_MSGTRANSFER), XML_MSGTRANSFER + "Template", false));
        buffer.append("</table>");
        buffer.append("</div>");

        // create form fields
        buffer.append("\r\n\r\n<form id=\"bla\" name=\"bla\" method=\"post\">");

        // @see CUI/default.jsp
        buffer.append("\r\n<div id=\"main_content\"><div>");
        //<%-- header image on the top right --%>
        buffer.append(
                "<img src=\"images/logo_klinik.jpg\" alt=\"Krankenhaus\" style=\"float:right;margin-bottom:1em;\">");
        //<%-- the Perikles logo and language selection --%>
        buffer.append("<img src=\"images/logo-133x20.jpg\" alt=\"Perikles\">");

        // localisation
        for (int i = 0; i < LANGUAGES.length; i++) {
            String l = LANGUAGES[i];
            buffer.append(i > 0 ? "&nbsp;" : "");
            if (l.equals(config.getLanguage())) {
                buffer.append("<img src=\"images/").append(l).append("-32-16.gif\" style=\"margin:1px 0 0 6px;\"");
            } else {
                buffer.append("<input type=\"image\" src=\"images/").append(l).append(
                        "-32-16.gif\" style=\"margin:1px 0 0 10px;\" name=\"LocalePic\" id=\"LocalePic\" value=\"")
                        .append(l).append("\"");
                buffer.append(" onclick=\" document.getElementById('" + LANGUAGE_ATTRIBUTE_NAME
                        + "').value=this.value; return saveTab();\"");
                buffer.append(" onkeyup=\" document.getElementById('" + LANGUAGE_ATTRIBUTE_NAME
                        + "').value=this.value; return saveTab();\"");
            }
            buffer.append("/>");
        }

        buffer.append("<div id=\"userPanel\"><span class=\"ui-widget-header selected\">");
        buffer.append("<span>" + userName + "</span>");
        buffer.append(config.getLocalizedString("titleSchedulingPage"));
        buffer.append("</span></div>");
        buffer.append("<div class=\"clear\"></div>");

        // general warnings and errors
        if (XMLUtils.hasWarnings(rup.getRootElement())) {
            buffer.append("<div class=\"warningInput\"><span class=\"ui-corner-all\">");
            buffer.append(config.getLocalizedString("msgRUPWarning"));

            String warning = config.getLocalizedJSONString(XMLUtils.getWarningValue(rup.getRootElement()));
            if (warning != null) {
                buffer.append(warning);
            }
            buffer.append("</span></div>\r\n");
        }

        if (XMLUtils.hasErrors(rup.getRootElement())) {
            buffer.append("<div class=\"errorInput\"><span class=\"ui-corner-all\">");
            buffer.append(config.getLocalizedString("msgRUPError"));

            String error = config.getLocalizedJSONString(XMLUtils.getErrorValue(rup.getRootElement()));
            if (error != null) {
                buffer.append(error);
            }

            buffer.append("</span></div>\r\n");
        }

        // title of custom form
        buffer.append("<h1 title=\"'" + cas.getCaseId() + "'\">");
        buffer.append(config.getLocalizedString("case") + ": ");
        buffer.append(cas.getCaseId());
        buffer.append("</h1>");

        buffer.append("<input type=\"hidden\" name=\"" + LANGUAGE_ATTRIBUTE_NAME + "\" id=\""
                + LANGUAGE_ATTRIBUTE_NAME + "\" value=\"\"/>\r\n\r\n");

        // rescheduling information fields
        if (!isShortForm) {
            buffer.append("<input type=\"hidden\" name=\"reschedulingKey\" id=\"reschedulingKey\" value=\"\"/>");
        }

        // prepare tabs
        buffer.append("<div id=\"tabs\">");
        buffer.append("<ul>");

        for (Element activity : activities) {
            String activityName = activity.getChildText(XML_ACTIVITYNAME);
            String clazz = "";

            if (XMLUtils.hasErrors(activity)) {
                clazz = " class=\"" + CSS_ERRORTEXT + "\"";
            } else if (XMLUtils.hasWarnings(activity)) {
                clazz = " class=\"" + CSS_WARNINGTEXT + "\"";
            }

            buffer.append("<li><a href=\"#" + activityName + "\"><span" + clazz + ">");
            buffer.append(config.getLocalizedString(activityName));
            buffer.append("</span></a></li>");
        }
        buffer.append("</ul>");

        buffer.append("<input type=\"hidden\" name=\"selectedTab\" id=\"selectedTab\" value=\"\"/>");

        String selectedTab = request.getParameter("selectedTab");
        bufferBottom.append(" $(\"#tabs\").tabs('select', " + (selectedTab == null ? 0 : selectedTab) + ");\r\n");

        // loop over activities
        for (int i = 0; i < activities.size(); i++) {
            Element activity = activities.get(i);
            String activityName = activity.getChildText(XML_ACTIVITYNAME);

            buffer.append("\r\n\r\n");
            buffer.append("<div id=\"" + activityName + "\">");

            String key = XML_ACTIVITY + "_" + activityName;
            buffer.append("<fieldset>");
            buffer.append(
                    "<legend style=\"font-weight:bold;\">" + config.getLocalizedString("general") + "</legend>");
            buffer.append("<table>");
            buffer.append("<tr>");

            Element type = activity.getChild(XML_ACTIVITYTYPE);
            Element duration = activity.getChild(XML_DURATION);
            Element from = activity.getChild(XML_FROM);
            Element to = activity.getChild(XML_TO);
            buffer.append(
                    getInputColumn(type, type.getName(), null, key, XMLUtils.isVisibleFromSchema(type.getName())));
            buffer.append(getInputColumn(duration, duration.getName(), to, key,
                    XMLUtils.isVisibleFromSchema(duration.getName())));
            if (isShortForm) {

                // show FROM only at first activity (if i=0)
                buffer.append(getInputColumn(from, "Date", to, key,
                        i == 0 && XMLUtils.isVisibleFromSchema(from.getName())));
                buffer.append("<input type=\"hidden\" name=\"reschedulingKey\" id=\"reschedulingKey\" value=\""
                        + key + "_" + from.getName() + "\"/>");
            } else {
                buffer.append(getInputColumn(from, from.getName(), to, key,
                        XMLUtils.isVisibleFromSchema(from.getName())));
                buffer.append(
                        getInputColumn(to, to.getName(), from, key, XMLUtils.isVisibleFromSchema(to.getName())));
            }

            buffer.append("</tr>");
            buffer.append("</table>");
            buffer.append("</fieldset>");

            buffer.append(getListOf(activity, XML_RESERVATION, true));
            buffer.append(getListOf(activity, XML_UTILISATIONREL, !isShortForm));
            buffer.append(getListOf(activity, XML_MSGTRANSFER, !isShortForm));

            buffer.append("</div>");
        }

        bufferTop.append(" sessionHandle=\"" + ResourceServiceInterface.getInstance().getHandle() + "\";\r\n");
        bufferTop.append(" count=" + count + ";\r\n");
        bufferTop.append(" msgAjaxError=\"" + config.getLocalizedString("msgAjaxError") + "\";\r\n");
        bufferTop
                .append(" msgCancelWithoutSave=\"" + config.getLocalizedString("msgCancelWithoutSave") + "\";\r\n");
        bufferTop.append(" ajaxCache={};\r\n"); // cache reload bei jedem aufruf

        buffer.append("</div>");

        buffer.append("\r\n\r\n<input type=\"submit\" name=\"Cancel\" id=\"Cancel\" value=\""
                + config.getLocalizedString("cancelButton") + "\"");
        buffer.append(" onclick=\"return dontSave(event);\"");
        buffer.append(" onkeyup=\"return dontSave(event);\"");
        buffer.append(" title=\"" + config.getLocalizedString("AbortAndReturn") + "\"");
        buffer.append(
                " class=\"abort ui-button ui-widget ui-state-default ui-button-text-only ui-corner-left ui-button-text\"");
        buffer.append("/>");

        buffer.append("<input type=\"submit\" name=\"Save\" id=\"Save\" value=\""
                + config.getLocalizedString("saveButton") + "\"");
        buffer.append(haveTosave ? " disabled=false" : " disabled=true");
        buffer.append(" onclick=\"return saveTab();\"");
        buffer.append(" onkeyup=\"return saveTab();\"");
        buffer.append(" title=\"" + config.getLocalizedString("Save") + "\"");
        buffer.append(" class=\"ui-button ui-widget ui-state-default ui-button-text-only ui-button-text\"");
        buffer.append("/>");

        bufferBottom.append(" enableButton('Save', " + haveTosave + ");\r\n");

        // confirm reservations button
        if (canConfirmReservations) {
            buffer.append("<input type=\"submit\" name=\"ConfirmReservations\" id=\"ConfirmReservations\" value=\""
                    + config.getLocalizedString("confirmReservationsButton") + "\"");
            buffer.append(" onclick=\"return saveTab();\"");
            buffer.append(" onkeyup=\"return saveTab();\"");
            buffer.append(" title=\"" + config.getLocalizedString("confirmReservations") + "\"");
            buffer.append(" class=\"ui-button ui-widget ui-state-default ui-button-text-only ui-button-text\"");
            buffer.append("/>");
        }

        buffer.append("<input type=\"submit\" name=\"Complete\" id=\"Complete\" value=\""
                + config.getLocalizedString("completeButton") + "\"");
        buffer.append(" onclick=\"return saveTab();\"");
        buffer.append(" onkeyup=\"return saveTab();\"");
        buffer.append(" title=\"" + config.getLocalizedString("CompleteAndReturn") + "\"");
        buffer.append(
                " class=\"ui-button ui-widget ui-state-default ui-button-text-only ui-corner-right ui-button-text\"");
        buffer.append("/>");

        buffer.append("<input type=\"hidden\" name=\"CompleteForce\" id=\"CompleteForce\" value=\"\"/>");

        buffer.append("</div>");
        buffer.append("</div>");
        buffer.append("</form>");

        return buffer;
    }

    private StringBuffer getInputColumn(Element field, String name2Show, Element dependingField, String key,
            boolean visible) {
        StringBuffer buffer = new StringBuffer();

        String fieldKey = key + "_" + field.getName();

        List<Element> list = field.getChildren();
        if (list.isEmpty()) { //no more children
            String n = field.getName(), style = "";
            if (!visible || n.equals(XML_ROLE) || n.equals(XML_CAPABILITY) || n.equals(XML_CATEGORY)
                    || n.equals(XML_SUBCATEGORY)) {
                style += "display:none;"; // not visible till "enableResourceType"
            }

            buffer.append("\r\n");

            buffer.append("<td align=\"right\" valign=\"middle\"");
            buffer.append(style.isEmpty() ? "" : " style=\"" + style + "\"");
            buffer.append(">" + config.getLocalizedString(name2Show) + "</td>");

            buffer.append("<td align=\"right\" valign=\"middle\" class=\"inputTD\"");
            buffer.append(style.isEmpty() ? "" : " style=\"" + style + "\"");
            buffer.append(">");
            buffer.append(getInputHTML(field, dependingField, fieldKey));
            buffer.append("</td>");
            buffer.append("<td align=\"left\" valign=\"middle\" class=\"inputTDUnit\"");
            buffer.append(style.isEmpty() ? "" : " style=\"" + style + "\"");
            buffer.append(">");
            String unit = config.getLocalizedString(XMLUtils.getUnitFromSchema(field.getName()));
            buffer.append(unit == null ? "" : unit);
            buffer.append("</td>");

        } else {
            String resKey = fieldKey + "_" + XML_RESOURCE_TYPE;
            Boolean resourceTypeRole = null;

            if (wrapBeforeResource) {
                buffer.append("</tr><tr>");
            }

            // get resource type TODO@tbe: radiobox aus request auswerten
            for (Element childField : list) {
                String childName = childField.getName();

                if (resourceTypeRole != null) {
                    break;
                } else if (childName.equals(XML_ID)) {
                    if (childField.getText().startsWith("PA-")) {
                        resourceTypeRole = true;
                    } else if (childField.getText().startsWith("NH-")) {
                        resourceTypeRole = false;
                    }
                } else if (childName.equals(XML_ROLE) || childName.equals(XML_CAPABILITY)) {
                    if (!childField.getText().isEmpty()) {
                        resourceTypeRole = true;
                    }
                } else if (childName.equals(XML_CATEGORY) || childName.equals(XML_SUBCATEGORY)) {
                    if (!childField.getText().isEmpty()) {
                        resourceTypeRole = false;
                    }
                }
            }

            for (Element childField : list) {
                if (childField.getName().equals(XML_ID)) {
                    int levelsUp = wrapBeforeResource ? 6 : 2;

                    buffer.append("\r\n");

                    // insert radio group for human and non-human before
                    if (wrapBeforeResource) {
                        buffer.append("<td align=\"right\" valign=\"middle\"");
                        buffer.append(">" + config.getLocalizedString("resource") + "</td>");
                    }

                    buffer.append(
                            "<td align=\"right\" valign=\"bottom\" class=\"inputTD\" style=\"white-space:nowrap;\">"); // width=\"300\"

                    buffer.append("<input type=\"radio\" name=\"" + resKey + "\" id=\"" + resKey + "-human\"");
                    buffer.append(" value=\"human\"");
                    buffer.append(" onclick=\"");
                    buffer.append(" enableButton('Save', true);");
                    buffer.append(" enableResourceType(this" + Utils.copy(".parentNode", levelsUp) + ".id + '_"
                            + XML_RESOURCE + "_" + XML_ROLE + "');");
                    buffer.append(" enableResourceType(this" + Utils.copy(".parentNode", levelsUp) + ".id + '_"
                            + XML_RESOURCE + "_" + XML_CAPABILITY + "');");
                    buffer.append(" disableResourceType(this" + Utils.copy(".parentNode", levelsUp) + ".id + '_"
                            + XML_RESOURCE + "_" + XML_CATEGORY + "');");
                    buffer.append(" disableResourceType(this" + Utils.copy(".parentNode", levelsUp) + ".id + '_"
                            + XML_RESOURCE + "_" + XML_SUBCATEGORY + "');");
                    buffer.append(
                            " actualizeDropDownBox('" + XML_RESOURCE_TYPE + "-human','" + XML_ID + "',this.id);");
                    buffer.append("\"");
                    buffer.append(">&nbsp;" + config.getLocalizedString("human"));
                    buffer.append("&nbsp;");

                    buffer.append("<input type=\"radio\" name=\"" + resKey + "\" id=\"" + resKey + "-non-human\"");
                    buffer.append(" value=\"non-human\"");
                    buffer.append(" onclick=\"");
                    buffer.append(" enableButton('Save', true);");
                    buffer.append(" disableResourceType(this" + Utils.copy(".parentNode", levelsUp) + ".id + '_"
                            + XML_RESOURCE + "_" + XML_ROLE + "');");
                    buffer.append(" disableResourceType(this" + Utils.copy(".parentNode", levelsUp) + ".id + '_"
                            + XML_RESOURCE + "_" + XML_CAPABILITY + "');");
                    buffer.append(" enableResourceType(this" + Utils.copy(".parentNode", levelsUp) + ".id + '_"
                            + XML_RESOURCE + "_" + XML_CATEGORY + "');");
                    buffer.append(" enableResourceType(this" + Utils.copy(".parentNode", levelsUp) + ".id + '_"
                            + XML_RESOURCE + "_" + XML_SUBCATEGORY + "');");
                    buffer.append(" actualizeDropDownBox('" + XML_RESOURCE_TYPE + "-non-human','" + XML_ID
                            + "',this.id);");
                    buffer.append("\"");
                    buffer.append(">&nbsp;" + config.getLocalizedString("non-human"));

                    buffer.append("</td>");

                    if (wrapBeforeResource) {
                        buffer.append("<td align=\"left\" valign=\"middle\" class=\"inputTDUnit\"/>");
                    }

                    if (resourceTypeRole == null || resourceTypeRole) {
                        dependingField = new Element("human"); // set dependingField for ID
                        dependingField.setText("human");

                        bufferBottom.append(" enableResourceType('" + fieldKey + "_" + XML_ROLE + "');\r\n");
                        bufferBottom.append(" enableResourceType('" + fieldKey + "_" + XML_CAPABILITY + "');\r\n");
                        bufferBottom.append(" disableResourceType('" + fieldKey + "_" + XML_CATEGORY + "');\r\n");
                        bufferBottom
                                .append(" disableResourceType('" + fieldKey + "_" + XML_SUBCATEGORY + "');\r\n");
                        bufferBottom.append(" document.getElementById('" + resKey + "-human').checked = true;\r\n");
                        bufferBottom.append(" actualizeDropDownBox('" + XML_RESOURCE_TYPE + "-human','" + XML_ID
                                + "','" + fieldKey + "_" + XML_ID + "');\r\n");
                    } else {
                        dependingField = new Element("non-human"); // set dependingField for ID
                        dependingField.setText("non-human");

                        bufferBottom.append(" disableResourceType('" + fieldKey + "_" + XML_ROLE + "');\r\n");
                        bufferBottom.append(" disableResourceType('" + fieldKey + "_" + XML_CAPABILITY + "');\r\n");
                        bufferBottom.append(" enableResourceType('" + fieldKey + "_" + XML_CATEGORY + "');\r\n");
                        bufferBottom.append(" enableResourceType('" + fieldKey + "_" + XML_SUBCATEGORY + "');\r\n");
                        bufferBottom
                                .append(" document.getElementById('" + resKey + "-non-human').checked = true;\r\n");
                        bufferBottom.append(" actualizeDropDownBox('" + XML_RESOURCE_TYPE + "-non-human','" + XML_ID
                                + "','" + fieldKey + "_" + XML_ID + "');\r\n");
                    }
                }

                buffer.append(getInputColumn(childField, childField.getName(), dependingField, fieldKey,
                        XMLUtils.isVisibleFromSchema(childField.getName())));
                dependingField = childField;
            }
        }

        return buffer;
    }

    private StringBuffer getInputHTML(Element field, Element dependingField, String key) {
        StringBuffer tag1 = new StringBuffer(), tag2 = new StringBuffer(), tag3 = new StringBuffer();

        String n = field.getName();
        String value = field.getText();
        String title = "", onclick = "", onchange = "", onpropertychange = "", cssClass = "", style = "";
        String dependingFieldKey = null, dependingFieldValue = null;
        String fromFieldKey = null, durationFieldKey = null;
        String keyPart = key.substring(0, key.lastIndexOf("_"));
        boolean readonly = false;
        boolean disabled = XMLUtils.isReadonlyFromSchema(n);

        // is field no more editable (if SOU or EOU)
        Element activity = field.getParentElement();
        while (activity != null && !activity.getName().equals(XML_ACTIVITY)) {
            activity = activity.getParentElement();
        }
        boolean eou = isRequestTypeEOU(activity);
        boolean sou = eou || isRequestTypeSOU(activity);

        ArrayList<String> enumerations = new ArrayList<String>();
        try {
            ArrayList<Element> restrictions = new ArrayList<Element>();
            cssClass = validateElement(field, restrictions);
            for (Element restriction : restrictions) {
                if (restriction.getName().equals("enumeration")) {
                    enumerations.add(restriction.getAttributeValue("value"));
                }
            }

            if (cssClass == null)
                cssClass = "";
        } catch (Exception e) {
            _log.error("cannot get css class for element: " + n, e);
        }

        if (dependingField != null) {
            durationFieldKey = keyPart + "_" + XML_DURATION;
            fromFieldKey = keyPart + "_" + XML_FROM;
            dependingFieldValue = dependingField.getText();
            dependingFieldKey = keyPart + "_" + dependingField.getName();
        }

        // set field layout
        if (!enumerations.isEmpty() || XMLUtils.isEnumerationFromSchema(n)) {
            tag1.append("<select size=\"1\"");

            if (XMLUtils.isEnumerationFromSchema(n)) {
                if (n.equals(XML_OTHERACTIVITYNAME)) {
                    for (Element activityElem : activities) {
                        String name = activityElem.getChildText(XML_ACTIVITYNAME);
                        tag2.append("<option value=\"" + name + "\"");
                        tag2.append(name.equals(value) ? " selected=\"selected\"" : "");
                        tag2.append(">" + config.getLocalizedString(name) + "</option>");
                    }
                } else if (n.equals(XML_ACTIVITYTYPE)) {
                    try {
                        tag2.append("<option value=\"\"");
                        tag2.append("".equals(value) ? " selected=\"selected\"" : "");
                        tag2.append(">" + config.getLocalizedString("&lt;msgNewEntry&gt;") + "</option>");

                        List<String> activityTypes = ss.getActivityTypes(activity.getChildText(XML_ACTIVITYNAME),
                                value);
                        for (String activityType : activityTypes) {
                            tag2.append("<option value=\"" + activityType + "\"");
                            tag2.append(activityType.equals(value) ? " selected=\"selected\"" : "");
                            tag2.append(">" + config.getLocalizedString(activityType) + "</option>");
                        }
                    } catch (Exception e) {
                        XMLUtils.addWarningValue(field, "msgActivityTypesWarning");
                    }

                    // activate if:
                    // 1. 'new entry' was selected or
                    // 2. 'new entry' was clicked and no more options than 'new entry' exists in selectbox
                    onchange += " comboBox(this,0);"; //
                    onpropertychange += " comboBox(this,0);";
                    onclick += " comboBox(this,1);";
                } else {
                    tag2.append("<script type=\"text/javascript\">");
                    tag2.append("writeDropDownBox('" + n + "','" + dependingFieldValue + "','" + value + "','" + key
                            + "');");
                    tag2.append("</script>");
                }
            } else {
                for (String s : enumerations) {
                    tag2.append("<option value=\"" + s + "\"");
                    tag2.append(s.equals(value) ? " selected=\"selected\"" : "");
                    tag2.append(">" + config.getLocalizedString(s) + "</option>");
                }
            }

            tag3.append("</select>");
        } else if (cssClass.equals(CSS_DATEINPUT)) {
            try {
                Date date = Utils.string2Date(value, Utils.DATETIME_PATTERN_XML);
                value = Utils.date2String(date, isShortForm ? Utils.DATE_PATTERN : Utils.DATETIME_PATTERN);
            } catch (ParseException e) {
                //_log.error("cannot parse " + value);
            }

            tag1.append("<input type=\"text\" value=\"" + value + "\"");

            String showCalendar = " displayCalendar(document.getElementById('" + key + "'),'";
            if (isShortForm) {
                showCalendar += Utils.getJsCalendarFormat(Utils.DATE_PATTERN) + "',this);return false;";
            } else {
                showCalendar += Utils.getJsCalendarFormat(Utils.DATETIME_PATTERN) + "',this,true);return false;";
            }
            readonly = true;
            onclick += (disabled ? "" : showCalendar);

            tag3.append("</input>");
        } else if (cssClass.equals(CSS_DURATIONINPUT)) { // TODO@tbe: not only minutes, use jquery.slider
            tag1.append("<input type=\"text\" value=\"" + Utils.stringXMLDuration2stringMinutes(value) + "\"");
            tag3.append("</input>");
        } else {
            tag1.append("<input type=\"text\" value=\"" + value + "\"");
            tag3.append("</input>");
        }

        // some javascript functions for convenience
        if (n.equals(XML_FROM)) {
            if (!isShortForm) {
                onchange += " addMinutes2DateField('" + dependingFieldKey + "','" + key + "','" + durationFieldKey
                        + "',1,false,'" + Utils.getJsCalendarFormat(Utils.DATETIME_PATTERN) + "');";
                onchange += " submitRescheduling('" + dependingFieldKey + "');";
            } else {
                onchange += " setRescheduling('" + key + "');";
            }
            disabled = disabled || sou;
        } else if (n.equals(XML_TO)) {
            if (!isShortForm) {
                onchange += " addMinutes2DateField('" + dependingFieldKey + "','" + key + "','" + durationFieldKey
                        + "',-1,false,'" + Utils.getJsCalendarFormat(Utils.DATETIME_PATTERN) + "');";
                onchange += " submitRescheduling('" + dependingFieldKey + "');";
            }
            disabled = disabled || eou;
        } else if (n.equals(XML_ID) || n.equals(XML_ROLE) || n.equals(XML_CAPABILITY) || n.equals(XML_CATEGORY)
                || n.equals(XML_SUBCATEGORY)) {
            String statusFieldKey = keyPart.substring(0, keyPart.lastIndexOf("_") + 1) + XML_STATUS;
            onchange += " document.getElementById('" + statusFieldKey + "').selectedIndex=0;"; // set to "unknown"

            if (n.equals(XML_CATEGORY)) {
                onchange += " actualizeDropDownBox('" + XML_CATEGORY + "','" + XML_SUBCATEGORY + "',this.id);";
            }

            disabled = disabled || sou || eou;
        } else if (n.equals(XML_STATUS) || n.equals(XML_STATUSTOBE)) {
            disabled = disabled || sou || eou;
        } else if (n.equals(XML_DURATION)) {
            if (!isShortForm) {
                onchange += " addMinutes2DateField('" + dependingFieldKey + "','" + fromFieldKey + "','"
                        + durationFieldKey + "',1,true,'" + Utils.getJsCalendarFormat(Utils.DATETIME_PATTERN)
                        + "');";
                onchange += " submitRescheduling('" + dependingFieldKey + "');";
            }
            disabled = disabled || sou || eou;
        } else if (n.equals(XML_WORKLOAD)) {
            disabled = disabled || sou || eou;
        } else if (n.equals(XML_OTHERACTIVITYNAME) || n.equals(XML_MIN) || n.equals(XML_MAX)
                || n.equals(XML_THISUTILISATIONTYPE) || n.equals(XML_OTHERUTILISATIONTYPE)) {
            onchange += " setRescheduling('" + key + "');";
        }

        // show error and warning messages
        String error = config.getLocalizedJSONString(XMLUtils.getErrorValue(field));
        String warning = config.getLocalizedJSONString(XMLUtils.getWarningValue(field));

        if (error != null) {
            title = error;
            cssClass += " " + CSS_ERRORINPUT;
        } else {
            if (warning != null) {
                title = warning;
                cssClass += " " + CSS_WARNINGINPUT;
            }
        }

        if (XMLUtils.isRequiredFromSchema(n) || isShortForm) {
            cssClass += " " + CSS_REQUIRED;
        }
        tag1.append(disabled ? " disabled=\"disabled\"" : "");
        tag1.append(readonly ? " readonly=\"readonly\"" : "");

        tag1.append(" id=\"" + key + "\" name=\"" + key + "\"");
        tag1.append(title.isEmpty() ? "" : " title=\"" + title + "\"");
        tag1.append(cssClass.isEmpty() ? "" : " class=\"" + cssClass + "\"");
        tag1.append(style.isEmpty() ? "" : " style=\"" + style + "\"");
        tag1.append(" onchange=\"enableButton('Save', true);" + onchange + "\"");
        tag1.append(" onpropertychange=\"enableButton('Save', true);" + onpropertychange + "\"");
        tag1.append(onclick.isEmpty() ? "" : " onclick=\"" + onclick + "\"");
        tag1.append(">");

        return tag1.append(tag2).append(tag3);
    }

    /**
     * gets rows for each element of type reservation or utilisationRelation
     *
     * @param listElementName
     * @return
     */
    private StringBuffer getListOf(Element activity, String listElementName, boolean visible) {
        boolean disabledButton = false;
        if (listElementName.equals(XML_RESERVATION)) {
            disabledButton = isRequestTypeEOU(activity) || isRequestTypeSOU(activity);
        } else if (listElementName.equals(XML_UTILISATIONREL)) {
            disabledButton = true; // TODO@tbe: erstmal nur von einer Relation pro Aktivitt ausgehen
        }

        StringBuffer buffer = new StringBuffer();
        String templateName = listElementName + "Template";
        String insertName = activity.getChildText(XML_ACTIVITYNAME) + "_" + templateName + "_insert";
        String key = XML_ACTIVITY + "_" + activity.getChildText(XML_ACTIVITYNAME) + "_" + listElementName + "_#";

        buffer.append("\r\n\r\n<fieldset");
        buffer.append(visible ? "" : " style=\"display: none\"");
        buffer.append(">");

        buffer.append("<legend style=\"font-weight:bold;\">" + config.getLocalizedString(listElementName + "s")
                + "</legend>");
        buffer.append("<table>");
        buffer.append("<tr>");
        buffer.append(getAddButton(templateName, key, insertName, disabledButton));
        buffer.append("</tr>");

        List<Element> elements = (List<Element>) activity.getChildren(listElementName);
        for (int i = 1; i <= elements.size(); i++) {
            count++;
            Element resOrUtil = elements.get(i - 1);

            buffer.append(getErrorRow(resOrUtil, key + count));
            buffer.append(getRow(resOrUtil, key + count, disabledButton));
        }

        buffer.append("\r\n<tr style=\"display: none\" id=\"" + insertName + "\"><td></td></tr>");
        buffer.append("</table>");
        buffer.append("</fieldset>");

        return buffer;
    }

    /**
     * gets row for a element of type reservation or utilisationRelation
     *
     * @param resOrUtil
     * @param key
     * @param disabledButton
     * @return
     */
    private StringBuffer getRow(Element resOrUtil, String key, boolean disabledButton) {
        StringBuffer buffer = new StringBuffer();
        String trStyle = count % 2 == 1 ? " style=\"background-color: #FFEEBB;\"" : "";
        buffer.append("\r\n\r\n<tr id=\"" + key + "\"" + trStyle + ">");
        if (wrapBeforeResource) {
            buffer.append("<td style=\"border: 2px groove threedface;\"><table><tr>");
        }

        // loop over all fields
        Element prevField = null; // if exist dependency to last field
        for (Element field : (List<Element>) resOrUtil.getChildren()) {
            buffer.append(getInputColumn(field, field.getName(), prevField, key,
                    XMLUtils.isVisibleFromSchema(field.getName())));
            prevField = field;
        }

        buffer.append(getAddRemoveButtons(key, disabledButton, wrapBeforeResource ? 6 : 2));

        if (wrapBeforeResource) {
            buffer.append("</tr></table></td>");
        }

        buffer.append("</tr>");

        return buffer;
    }

    /**
     * gets error row for a element of type reservation or utilisationRelation
     *
     * @param element
     * @param key
     * @return
     */
    private StringBuffer getErrorRow(Element element, String key) {
        StringBuffer buffer = new StringBuffer();
        String error = config.getLocalizedJSONString(XMLUtils.getErrorValue(element));
        if (error != null) {
            buffer.append("<tr id=\"" + key + "_error\"><td colspan=\"" + (getColspan(element) * 2 + 1)
                    + "\" style=\"text-align:left;\">");
            buffer.append("<div class=\"" + CSS_ERRORTEXT + "\">");
            buffer.append(error);
            buffer.append("</div></td></tr>");
        } else {
            String warning = config.getLocalizedJSONString(XMLUtils.getWarningValue(element));
            if (warning != null) {
                buffer.append("<tr id=\"" + key + "_warning\"><td colspan=\"" + (getColspan(element) * 2 + 1)
                        + "\" style=\"text-align:left;\">");
                buffer.append("<div class=\"" + CSS_WARNINGTEXT + "\">");
                buffer.append(warning);
                buffer.append("</div></td></tr>");
            }
        }

        return buffer;
    }

    private int getColspan(Element element) {
        int count = element.getChildren().isEmpty() ? 1 : 0;
        for (Element e : (List<Element>) element.getChildren()) {
            count += getColspan(e);
        }
        return count;
    }

    private StringBuffer getAddButton(String templateName, String key, String insertName, boolean disabled) {
        String onclickAdd = "addCloneBeforeInsert(document.getElementById('" + templateName + "'), " + "'" + key
                + "', document.getElementById('" + insertName + "')); enableButton('Save', true); return false;";

        StringBuffer buffer = new StringBuffer();
        buffer.append("<td>");

        buffer.append("\r\n<input type=\"image\" src=\"images/plus2.png\" alt=\""
                + config.getLocalizedString("addResourceButton") + "\"");

        buffer.append(" onclick=\"" + onclickAdd + "\"");
        buffer.append(" title=\"" + config.getLocalizedString("addResourceButton") + "\"");
        buffer.append(disabled ? " disabled=\"disabled\"" : "");

        buffer.append("/>");

        buffer.append("</td>");
        return buffer;
    }

    private StringBuffer getAddRemoveButtons(String key, boolean disabled, int levelsUp) {
        String onclickRemove = "removeFieldAndItsError(this" + Utils.copy(".parentNode", levelsUp)
                + "); enableButton('Save', true); return false;";

        StringBuffer buffer = new StringBuffer();
        buffer.append("<td style=\"text-align: right;\">");
        buffer.append("\r\n\r\n<input type=\"image\" src=\"images/minus2.png\" alt=\""
                + config.getLocalizedString("removeButton") + "\"");
        buffer.append(" onclick=\"" + onclickRemove + "\"");
        buffer.append(" title=\"" + config.getLocalizedString("removeButton") + "\"");
        buffer.append(disabled ? " disabled=\"disabled\"" : "");
        buffer.append("/>");

        buffer.append("</td>");
        return buffer;
    }

    private String getRequestType(Element activity) {
        return activity == null ? null : activity.getChildText(XML_REQUESTTYPE);
    }

    private boolean isRequestTypeSOU(Element activity) {
        String requestType = getRequestType(activity);
        return requestType != null && requestType.equals(UTILISATION_TYPE_BEGIN);
    }

    private boolean isRequestTypeEOU(Element activity) {
        String requestType = getRequestType(activity);
        return requestType != null && requestType.equals(UTILISATION_TYPE_END);
    }

    /**
     * TODO@tbe: can we generate this templates from XSD?
     *
     * @param elementName
     * @return
     */
    public static Element getTemplate(String elementName) {
        Element elem;
        if (XML_ACTIVITY.equals(elementName)) {
            elem = Utils
                    .string2Element("<Activity><ActivityName/><ActivityType/><Duration/><From/><To/></Activity>");
        } else if (XML_RESERVATION.equals(elementName)) {
            elem = Utils.string2Element("<Reservation><StatusToBe/><Status/><Workload/><Resource>"
                    + "<Id/><Role/><Capability/><Category/><SubCategory/></Resource><ReservationId/></Reservation>");
        } else if (XML_UTILISATIONREL.equals(elementName)) {
            elem = Utils.string2Element("<UtilisationRelation><ThisUtilisationType/>"
                    + "<OtherUtilisationType/><OtherActivityName/><Min/><Max/></UtilisationRelation>");
        } else if (XML_MSGTRANSFER.equals(elementName)) {
            elem = Utils.string2Element("<" + XML_MSGTRANSFER + "><" + XML_MSGDURATION + "/><" + XML_MSGREL + "/>"
                    + "<" + XML_MSGUTILISATIONTYPE + "/><" + XML_MSGTO + "/><" + XML_MSGBODY + "/></"
                    + XML_MSGTRANSFER + ">");
        } else {
            _log.warn("element " + elementName + " has no template");
            return new Element(elementName);
        }

        XMLUtils.setDefaults(elem);
        return elem;
    }

    /**
     * redirect to source
     */
    private void redirect(String source) throws IOException {
        session.removeAttribute("ExceptionMsg");
        session.removeAttribute("Exception");
        response.sendRedirect(response.encodeURL(source));
    }

    /**
     * redirect back to the worklist
     */
    private void redirect() throws IOException {
        redirect(source);
    }

    /**
     * shows error page
     */
    private void handleError(Throwable t) {
        try {
            _log.error("show error page: ", t);
            session.setAttribute("ErrorPageTitle", config.getLocalizedString("titleErrorPage"));
            session.setAttribute("Exception", t);

            String errorMsg = config.getLocalizedString("msgErrorPage");
            if (wirElement != null && wirElement.getName().equals("failure")) {
                String yawlErrorMsg = config.getLocalizedString(wirElement.getText());
                errorMsg += yawlErrorMsg.isEmpty() ? "" : ": " + yawlErrorMsg;
            }
            session.setAttribute("ExceptionMsg", errorMsg);

            String url = PropertyReader.getInstance().getYAWLProperty("WorkQueueGatewayClient.backEndURI");
            url = url.substring(0, url.indexOf("resourceService/") + 16);
            session.setAttribute("ErrorPageLoginText", config.getLocalizedString("msgErrorPageLoginText"));
            session.setAttribute("ErrorPageLoginLink", url + "faces/Login.jsp");
            session.setAttribute("ErrorPageWorkqueueText", config.getLocalizedString("msgErrorPageWorkqueueText"));
            session.setAttribute("ErrorPageWorkqueueLink", url + "faces/userWorkQueues.jsp");
        } catch (Throwable e) {
            _log.error("cannot configure error page", e);
        }

        try {
            response.sendRedirect(response.encodeURL("Error.jsp"));
        } catch (Throwable e) {
            _log.error("cannot find error page", e);
        }
    }

}