/*
 * Copyright (C) 2003-2007 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.services.cms.templates.impl;

import org.exoplatform.container.component.BaseComponentPlugin;
import org.exoplatform.container.configuration.ConfigurationManager;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.ObjectParameter;
import org.exoplatform.container.xml.ValueParam;
import org.exoplatform.services.cms.BasePath;
import org.exoplatform.services.cms.impl.DMSConfiguration;
import org.exoplatform.services.cms.impl.DMSRepositoryConfiguration;
import org.exoplatform.services.cms.impl.Utils;
import org.exoplatform.services.cms.templates.TemplateService;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;

import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.nodetype.NodeDefinition;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeIterator;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.nodetype.PropertyDefinition;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class TemplatePlugin extends BaseComponentPlugin {

  static final public String   DIALOGS                    = "dialogs";

  static final public String   VIEWS                      = "views";

  static final public String   SKINS                      = "skins";

  static final public String   DEFAULT_DIALOG             = "dialog1";

  static final public String   DEFAULT_VIEW               = "view1";

  static final String[]        UNDELETABLE_TEMPLATES      = { DEFAULT_DIALOG, DEFAULT_VIEW };

  static final public String   DEFAULT_DIALOGS_PATH       = "/" + DIALOGS + "/" + DEFAULT_DIALOG;

  static final public String   DEFAULT_VIEWS_PATH         = "/" + VIEWS + "/" + DEFAULT_VIEW;

  static final public String   NT_UNSTRUCTURED            = "nt:unstructured";

  static final public String   DOCUMENT_TEMPLATE_PROP     = "isDocumentTemplate";

  static final public String   TEMPLATE_LABEL             = "label";

  public static final String[] EXO_ROLES_DEFAULT          = new String[] { "*" };

  private static final String  NAME;

  private static final String  COMMENT_TEMPLATE;

  private static final String  HEADER_VIEW;

  private static final String  JAVA_HEADER_VIEW;

  private static final String  DEF_FIELD_PROPERTY;

  private static final String  FIELD_PROPERTY;

  private static final String  START_DIALOG_FORM;

  private static final String  END_DIALOG_FORM;

  private static final String  TD_LABEL;

  private static final String  TD_COMPONENT;

  private static final String  START_TABLE;

  private static final String  END_TABLE;

  private static final String  START_TR;

  private static final String  END_TR;

  private static final String  CHECK_PROPERTY;

  private static final String  GET_PROPERTY;

  private static final String  START_JAVA;

  private static final String  END_JAVA;

  private static final String  DEFAULT_CSS;

  private static final String  JCR_PRIMARY_TYPE = "jcr:primaryType";

  private static final String  JCR_MIXIN_TYPES = "jcr:mixinTypes";

  private RepositoryService repositoryService_;
  private ConfigurationManager  configManager_;
  private NodeHierarchyCreator nodeHierarchyCreator_;
  private String cmsTemplatesBasePath_ ;
  private InitParams params_ ;
  private String storedLocation_ ;
  private boolean autoCreateInNewRepository_=false;
  private static final Log LOG = ExoLogger.getLogger(TemplatePlugin.class.getName());
  private Set<String> configuredNodeTypes;

  private TemplateService templateService;

  static {

    COMMENT_TEMPLATE = "<%\n// Generate template for nodetype automatically\n%>\n";

    HEADER_VIEW = new StringBuilder("<style>\n")
                    .append("\t<% _ctx.include(uicomponent.getTemplateSkin(\"${NodeType}\", \"Stylesheet\")); %>\n")
                    .append("</style>\n").toString();

    JAVA_HEADER_VIEW = new StringBuilder("<%\n\tdef node = uicomponent.getNode();\n")
                        .append("\tdef name = node.getName();\n")
                        .append("\tdef values;\n")
                        .append("\tdef valueDisplay;\n%>").toString();

    DEF_FIELD_PROPERTY = "\t\t\t\t\t<%\n\t\t\t\t\t\tString[] fieldProperty; \n\t\t\t\t\t%>\n";

    FIELD_PROPERTY = "fieldProperty";

    START_DIALOG_FORM = new StringBuilder("<div class=\"UIForm FormLayout FormScrollLayout\">\n")
                        .append("\t<% uiform.begin();\n")
                        .append("\t   /* start render action*/\n")
                        .append("\t   if (uiform.isShowActionsOnTop()) uiform.processRenderAction();\n")
                        .append("\t   /* end render action*/\n")
                        .append("\t%> \n")
                        .append("\t\t<div class=\"HorizontalLayout\">\n").toString();

    NAME = new StringBuilder("\n\t\t\t\t\t<%\n")
            .append("\t\t\t\t\t\tString[] fieldName = [\"jcrPath=/node\", \"editable=if-null\", \"validate=empty,name\"];\n")
            .append("\t\t\t\t\t\tuicomponent.addTextField(\"name\", fieldName);\n")
            .append("\t\t\t\t\t%>\n").toString();

    START_TABLE = "\n\t\t\t<table class=\"UIFormGrid\">\n";

    START_TR = "\n\t\t\t\t<tr>\n";

    END_TR = "\t\t\t\t</tr>";

    START_JAVA = "\n\t\t\t\t<%\n";

    END_JAVA = "\n\t\t\t\t%>";

    CHECK_PROPERTY = "\t\t\t\t if (node.hasProperty(\"${propertyname}\")) {";

    GET_PROPERTY = "node.getProperty(\"${propertyname}\")";

    TD_LABEL = new StringBuilder("").append("\t\t\t\t\t<td class=\"FieldLabel\">")
                .append("<%=_ctx.appRes(\"${nodetypename}.dialog.label.${propertyname}\")%>")
                .append("</td>").toString();

    TD_COMPONENT = new StringBuilder("\n").append("\t\t\t\t\t<td class=\"FieldComponent\">")
                    .append("${contentcomponent}").append("\t\t\t\t\t</td>\n").toString();

    END_TABLE = "\n\t\t\t</table>\n";

    END_DIALOG_FORM = new StringBuilder("").append("\t\t</div>\n")
                        .append("\t<% /* start render action*/\n")
                        .append("\t   if (!uiform.isShowActionsOnTop()) uiform.processRenderAction();\n")
                        .append("\t   /* end render action*/\n")
                        .append("\t   uiform.end();\n\t%>\n").append("</div>").toString();

    DEFAULT_CSS = new StringBuilder(".UIFormGrid {")
                    .append("\n\tborder:1px solid #B7B7B7;")
                    .append("\n\tborder-collapse:collapse;")
                    .append("\n\tmargin:auto;")
                    .append("\n\tpadding-left:1px;")
                    .append("\n\ttable-layout:fixed;")
                    .append("\n}")
                    .append("\n\n.UIFormGrid .FieldLabel {")
                    .append("\n\tfont-weight:bold;")
                    .append("\n\twidth:auto;")
                    .append("\n}")
                    .append("\n\n.UIFormGrid td {")
                    .append("\n\tborder-left:1px solid #CCCCCC;")
                    .append("\n\tborder-right:1px solid #CCCCCC;")
                    .append("\n\tborder-top:1px solid #FFFFFF;")
                    .append("\n\tbackground:#F3F3F3 none repeat scroll 0 0;")
                    .append("\n\theight:20px;")
                    .append("\n\tline-height:20px;")
                    .append("\n\tpadding:4px;")
                    .append("\n}").toString();
  }

  /**
   * DMS configuration which used to store informations
   */
  private DMSConfiguration dmsConfiguration_;

  public TemplatePlugin(InitParams params,
                        RepositoryService jcrService,
                        ConfigurationManager configManager,
                        NodeHierarchyCreator nodeHierarchyCreator,
                        DMSConfiguration dmsConfiguration) throws Exception {
    nodeHierarchyCreator_ = nodeHierarchyCreator;
    repositoryService_ = jcrService;
    configManager_ = configManager;
    cmsTemplatesBasePath_ = nodeHierarchyCreator_.getJcrPath(BasePath.CMS_TEMPLATES_PATH);
    params_ = params;
    ValueParam locationParam = params_.getValueParam("storedLocation") ;
    storedLocation_ = locationParam.getValue();
    ValueParam param = params_.getValueParam("autoCreateInNewRepository");
    if(param!=null) {
      autoCreateInNewRepository_ = Boolean.parseBoolean(param.getValue());
    }
    dmsConfiguration_ = dmsConfiguration;
    templateService = WCMCoreUtils.getService(TemplateService.class);
  }

  public void init() throws Exception {
    configuredNodeTypes = new HashSet<String>();
    importPredefineTemplates() ;
  }

  @SuppressWarnings("rawtypes")
  private void addTemplate(TemplateConfig templateConfig, Node templatesHome, String storedLocation) throws Exception {
    Set<String> editedPredefinedNodeTypes = templateService.getAllEditedConfiguredNodeTypes();
    NodeTypeManager ntManager = templatesHome.getSession().getWorkspace().getNodeTypeManager() ;
    NodeTypeIterator nodetypeIter = ntManager.getAllNodeTypes();
    List<String> listNodeTypeName = new ArrayList<String>();
    while (nodetypeIter.hasNext()) {
      NodeType n1 = nodetypeIter.nextNodeType();
      listNodeTypeName.add(n1.getName());
    }
    List nodetypes = templateConfig.getNodeTypes();
    TemplateConfig.NodeType nodeType = null ;
    Iterator iter = nodetypes.iterator() ;
    while(iter.hasNext()) {
      nodeType = (TemplateConfig.NodeType) iter.next();
      if(Utils.getAllEditedConfiguredData(
        "ContentTemplateList", "EditedConfiguredTemplateList", true).contains(nodeType.getNodetypeName())
        || !listNodeTypeName.contains(nodeType.getNodetypeName())) {
        if (LOG.isErrorEnabled()) {
          LOG.error("The nodetype: " + nodeType.getNodetypeName() + " or its templates doesn't exist!");
        }
        continue;
      }
      
      configuredNodeTypes.add(nodeType.getNodetypeName());

      // Do not update predefined templates which was edited by user
      if (editedPredefinedNodeTypes.contains(nodeType.getNodetypeName())) continue;
      
      Node nodeTypeHome = null;
      nodeTypeHome = Utils.makePath(templatesHome, nodeType.getNodetypeName(), NT_UNSTRUCTURED);
      if(nodeType.getDocumentTemplate())
        nodeTypeHome.setProperty(DOCUMENT_TEMPLATE_PROP, true) ;
      else
        nodeTypeHome.setProperty(DOCUMENT_TEMPLATE_PROP, false) ;

      nodeTypeHome.setProperty(TEMPLATE_LABEL, nodeType.getLabel()) ;

      List dialogs = nodeType.getReferencedDialog();
      addNode(storedLocation, nodeType, dialogs, DIALOGS, templatesHome);

      List views = nodeType.getReferencedView();
      addNode(storedLocation, nodeType, views, VIEWS, templatesHome);

      List skins = nodeType.getReferencedSkin();
      if(skins != null) {
        addNode(storedLocation, nodeType, skins, SKINS, templatesHome);
      }
    }
  }

  public void setBasePath(String basePath) { cmsTemplatesBasePath_ = basePath ; }

  @SuppressWarnings("unchecked")
  private void importPredefineTemplates() throws Exception {
    ManageableRepository repository = repositoryService_.getCurrentRepository();
    DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig();
    String workspace = dmsRepoConfig.getSystemWorkspace();
    Session session = repository.getSystemSession(workspace) ;
    Node templatesHome = Utils.makePath(session.getRootNode(), cmsTemplatesBasePath_, NT_UNSTRUCTURED);
    TemplateConfig templateConfig = null ;
    Iterator<ObjectParameter> iter = params_.getObjectParamIterator() ;
    while(iter.hasNext()) {
      Object object = iter.next().getObject() ;
      if(!(object instanceof TemplateConfig)) {
        break ;
      }
      templateConfig = (TemplateConfig)object ;
      addTemplate(templateConfig,templatesHome,storedLocation_) ;
    }
    session.logout();
  }

  @SuppressWarnings("rawtypes")
  private void addNode(String basePath, TemplateConfig.NodeType nodeType, List templates, String templateType,
      Node templatesHome)  throws Exception {
    for (Iterator iterator = templates.iterator(); iterator.hasNext();) {
      TemplateConfig.Template template = (TemplateConfig.Template) iterator.next();
      String templateFileName = template.getTemplateFile();
      String path = basePath + templateFileName;
      InputStream in = configManager_.getInputStream(path);
      String nodeName = templateFileName.substring(templateFileName.lastIndexOf("/") + 1, templateFileName.indexOf("."));
      Node nodeTypeHome = null;
      if (!templatesHome.hasNode(nodeType.getNodetypeName())) {
        nodeTypeHome = Utils.makePath(templatesHome, nodeType.getNodetypeName(), NT_UNSTRUCTURED);
      } else {
        nodeTypeHome = templatesHome.getNode(nodeType.getNodetypeName());
      }
      Node specifiedTemplatesHome = null;
      try {
        specifiedTemplatesHome = nodeTypeHome.getNode(templateType);
      } catch(PathNotFoundException e) {
        specifiedTemplatesHome = Utils.makePath(nodeTypeHome, templateType, NT_UNSTRUCTURED);
      }
      if(!specifiedTemplatesHome.hasNode(nodeName)) {
        templateService.addTemplate(templateType,
                                    nodeType.getNodetypeName(),
                                    nodeType.getLabel(),
                                    nodeType.getDocumentTemplate(),
                                    nodeName,
                                    template.getParsedRoles(),
                                    in,
                                    templatesHome);
      }
    }
  }

  /**
   * Build default style sheet
   * @param nodeType
   * @return
   */
  public String buildStyleSheet(NodeType nodeType) {
    return COMMENT_TEMPLATE.concat(DEFAULT_CSS);
  }

  /**
   * Build string of dialog form template base on properties of nodetype
   * @param nodeType
   * @return
   */
  public String buildDialogForm(NodeType nodeType) throws ValueFormatException, RepositoryException {
    StringBuilder buildDialogForm = new StringBuilder(COMMENT_TEMPLATE);
    buildDialogForm.append(START_DIALOG_FORM).append(START_TABLE);
    buildDialogForm.append(START_TR);
    buildDialogForm.append(DEF_FIELD_PROPERTY);
    buildDialogForm.append(TD_LABEL.replace("${nodetypename}", nodeType.getName())
                                   .replace(":", "_")
                                   .replace("${propertyname}", "name"));
    buildDialogForm.append(TD_COMPONENT.replace("${contentcomponent}", NAME));
    buildDialogForm.append(END_TR);
    buildDialogForm.append(buildDialogNodeType(nodeType));
    buildDialogForm.append(END_TABLE);
    buildDialogForm.append(END_DIALOG_FORM);
    return buildDialogForm.toString();
  }

  /**
   * Build string of dialog template base on properties of nodetype
   * @param nodeType
   * @return
   */
  private String buildDialogNodeType(NodeType nodeType) throws ValueFormatException, RepositoryException {
    return buildDialogNodeType(nodeType, "/node/");
  }

  /**
   * Build string of dialog template base on properties of nodetype
   * @param nodeType
   * @return
   */
  private String buildDialogNodeType(NodeType nodeType, String jcrPath) throws ValueFormatException,
                                                                       RepositoryException {
    StringBuilder buildDialogNodeType = new StringBuilder();
    StringBuilder componentField;
    String propertyNameFormat;
    String propertyPath;
    String propertyId;
    StringBuilder params;
    StringBuilder validate;
    StringBuilder defaultValues;
    Value[] defaultValuesArr;
    // Render all property defined in NodeType
    PropertyDefinition[] prodefs = nodeType.getPropertyDefinitions();
    for (PropertyDefinition prodef : prodefs) {
      // Dismiss all auto created property
      String propertyName = prodef.getName();
      if (prodef.isAutoCreated() || "*".equals(propertyName)
          || JCR_PRIMARY_TYPE.equals(propertyName) || JCR_MIXIN_TYPES.equals(propertyName))
        continue;
      propertyNameFormat = propertyName.replace(":", "_");
      propertyPath = jcrPath.concat(propertyName);
      propertyId = propertyPath.replace(":", "_");
      componentField = new StringBuilder("\n\t\t\t\t\t\tuicomponent.addTextField(\"").append(propertyId)
                                                                                     .append("\", ");
      validate = new StringBuilder("validate=");
      buildDialogNodeType.append(START_TR);
      buildDialogNodeType.append(TD_LABEL.replace("${nodetypename}", nodeType.getName())
                                         .replace(":", "_")
                                         .replace("${propertyname}", propertyNameFormat));
      params = new StringBuilder("\t\t\t\t\t\t").append(FIELD_PROPERTY)
                                                .append(" = [\"jcrPath=")
                                                .append(propertyPath)
                                                .append("\"");
      if (prodef.isMultiple()) {
        params.append(", \"multiValues=true\"");
      }

      if (prodef.isMandatory()) {
        validate.append("empty,");
      }

      // Select component field base on required type
      switch (prodef.getRequiredType()) {

      case PropertyType.BOOLEAN :
        params.append(", \"options=true,false\"");
        componentField = new StringBuilder("\n\t\t\t\t\t\tuicomponent.addSelectBoxField(\"").append(propertyId)
                                                                                            .append("\", ");
        break;

      case PropertyType.STRING :
        break;

      case PropertyType.DATE :
        validate.append("datetime,");
        params.append(", \"options=displaytime\", \"visible=true\"");
        componentField = new StringBuilder("\n\t\t\t\t\t\tuicomponent.addCalendarField(\"").append(propertyId)
                                                                                           .append("\", ");
        break;

      case PropertyType.LONG :
        validate.append("number,");
        break;

      case PropertyType.DOUBLE :
        validate.append("number,");
        break;

      case PropertyType.REFERENCE :
        params.append(", \"reference=true\", \"editable=false\"");
        break;

      case PropertyType.BINARY :
        componentField = new StringBuilder("\n\t\t\t\t\t\tuicomponent.addUploadField(\"").append(propertyId)
                                                                                         .append("\", ");
        break;

      default:
        break;
      }

      defaultValuesArr = prodef.getDefaultValues();
      if (defaultValuesArr != null) {
        defaultValues = new StringBuilder("defaultValues=");
        for(Value value : defaultValuesArr) {
          defaultValues.append(value.getString()).append(",");
        }
        if (defaultValues.indexOf(",") > -1) {
          params.append(", \"").append(defaultValues.deleteCharAt(defaultValues.length() - 1)).append("\"");
        }
      }

      if (validate.indexOf(",") > -1) {
        params.append(", \"").append(validate.deleteCharAt(validate.length() - 1)).append("\"");
      }

      params.append("];");
      componentField.append(FIELD_PROPERTY).append(");");
      buildDialogNodeType.append(TD_COMPONENT.replace("${contentcomponent}",
                                                      START_JAVA.concat(params.append(componentField)
                                                                              .append(END_JAVA)
                                                                              .append("\n")
                                                                              .toString())));
      buildDialogNodeType.append(END_TR);
    }

    // Render all property for all Child Node if have by call recursive
    NodeDefinition[] childdefs = nodeType.getChildNodeDefinitions();
    for (NodeDefinition childdef : childdefs) {
      if (childdef != null) {
        for (NodeType requiredNodeType : childdef.getRequiredPrimaryTypes()) {
          if (childdef.getName().equals("*")) {
            jcrPath = jcrPath.concat(childdef.getRequiredPrimaryTypes()[0].getName()).concat("/");
          } else {
            jcrPath = jcrPath.concat(childdef.getName()).concat("/");
          }
          buildDialogNodeType.append(buildDialogNodeType(requiredNodeType, jcrPath));
        }
      }
    }
    return buildDialogNodeType.toString();
  }

  /**
   * Build string of view template form base on properties of nodetype
   * @param nodeType
   * @return
   */
  public String buildViewForm(NodeType nodeType) {
    StringBuilder buildViewForm = new StringBuilder(COMMENT_TEMPLATE);
    buildViewForm.append(HEADER_VIEW.replace("${NodeType}", nodeType.getName()));
    buildViewForm.append(JAVA_HEADER_VIEW);
    buildViewForm.append("\n\t\t<div id=\"$uicomponent.id\">");
    buildViewForm.append(START_TABLE);
    buildViewForm.append(START_TR);
    buildViewForm.append(TD_LABEL.replace("${nodetypename}", nodeType.getName())
                                 .replace(":", "_")
                                 .replace("${propertyname}", "name"));
    buildViewForm.append(TD_COMPONENT.replace("${contentcomponent}\t\t\t\t\t", "${name}"));
    buildViewForm.append(END_TR);
    buildViewForm.append(buildViewNodeType(nodeType));
    buildViewForm.append(END_TABLE);
    buildViewForm.append("\t\t</div>");
    return buildViewForm.toString();
  }

  /**
   * Build string of view template base on properties of nodetype
   * @param nodeType
   * @return
   */
  private String buildViewNodeType(NodeType nodeType) {
    StringBuilder buildViewNodeType = new StringBuilder();
    String label = TD_LABEL.replace("${nodetypename}", nodeType.getName().replace(":", "_"));
    PropertyDefinition[] prodefs = nodeType.getPropertyDefinitions();
    for (PropertyDefinition prodef : prodefs) {
      buildViewNodeType.append(START_JAVA).append(CHECK_PROPERTY.replace("${propertyname}", prodef.getName())).append(END_JAVA);
      buildViewNodeType.append(START_TR);
      buildViewNodeType.append(label.replace("${propertyname}", prodef.getName().replace(":", "_")));
      buildViewNodeType.append(START_JAVA);
      if (prodef.getRequiredType() == PropertyType.BINARY) {
        if (prodef.isMultiple()) {
          buildViewNodeType.append("\t\t\t\t\t// Render for multi value;\n");
          buildViewNodeType.append("\t\t\t\t\tvalues = ")
                           .append(GET_PROPERTY.replace("${propertyname}", prodef.getName()))
                           .append(".getValues()")
                           .append(";\n");
          buildViewNodeType.append("\t\t\t\t\tvalueDisplay = \"\";\n");
          buildViewNodeType.append("\t\t\t\t\tfor(value in values) {\n" );
          buildViewNodeType.append("\t\t\t\t\t\tvalueDisplay += \"BINARY DATA\" + \",\";\n" );
          buildViewNodeType.append("\t\t\t\t\t}\n" );
          buildViewNodeType.append("\t\t\t\t\tif (valueDisplay.length() > 0 && valueDisplay.indexOf(\",\") > -1) "
              + "valueDisplay = valueDisplay.substring(0, valueDisplay.length() - 1);");

        } else {
          buildViewNodeType.append("\t\t\t\t\t// Render for single value;\n");
          buildViewNodeType.append("\t\t\t\t\tvalueDisplay = \"BINARY DATA\"");
        }
      } else {
        if (prodef.isMultiple()) {
          buildViewNodeType.append("\t\t\t\t\t// Render for multi value;\n");
          buildViewNodeType.append("\t\t\t\t\tvalues = ")
                           .append(GET_PROPERTY.replace("${propertyname}", prodef.getName()))
                           .append(".getValues()")
                           .append(";\n");
          buildViewNodeType.append("\t\t\t\t\tvalueDisplay = \"\";\n");
          buildViewNodeType.append("\t\t\t\t\tfor(value in values) {\n" );
          buildViewNodeType.append("\t\t\t\t\t\tvalueDisplay += value.getString() + \",\";\n" );
          buildViewNodeType.append("\t\t\t\t\t}\n" );
          buildViewNodeType.append("\t\t\t\t\tif (valueDisplay.length() > 0 && valueDisplay.indexOf(\",\") > -1) "
              + "valueDisplay = valueDisplay.substring(0, valueDisplay.length() - 1);");

        } else {
          buildViewNodeType.append("\t\t\t\t\t// Render for single value;\n");
          buildViewNodeType.append("\t\t\t\t\tvalueDisplay = ")
                           .append(GET_PROPERTY.replace("${propertyname}", prodef.getName()))
                           .append(".getString();");
        }
      }
      buildViewNodeType.append(END_JAVA);
      buildViewNodeType.append(TD_COMPONENT.replace("${contentcomponent}\t\t\t\t\t", "${valueDisplay}"));
      buildViewNodeType.append(END_TR);
      buildViewNodeType.append(START_JAVA).append("\t\t\t\t\t}").append(END_JAVA);
    }
    return buildViewNodeType.toString();
  }

  public Set<String> getAllConfiguredNodeTypes() {
    return configuredNodeTypes;
  }
}
