package org.springframework.roo.addon.web.mvc.thymeleaf.addon;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.springframework.roo.addon.jpa.addon.entity.JpaEntityMetadata;
import org.springframework.roo.addon.jpa.annotations.entity.JpaRelationType;
import org.springframework.roo.addon.web.mvc.controller.addon.ControllerMetadata;
import org.springframework.roo.addon.web.mvc.controller.annotations.ControllerType;
import org.springframework.roo.addon.web.mvc.views.ViewContext;
import org.springframework.roo.addon.web.mvc.views.components.DetailEntityItem;
import org.springframework.roo.addon.web.mvc.views.components.EntityItem;
import org.springframework.roo.addon.web.mvc.views.components.FieldItem;
import org.springframework.roo.addon.web.mvc.views.components.MenuEntry;
import org.springframework.roo.addon.web.mvc.views.template.engines.AbstractFreeMarkerViewGenerationService;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.scanner.MemberDetails;
import org.springframework.roo.classpath.scanner.MemberDetailsScanner;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.model.RooJavaType;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.settings.project.ProjectSettingsService;
/**
*
* This class implements AbstractFreeMarkerViewGenerationService.
*
* As a result, view generation services could delegate on this one to generate
* views with Thymeleaf components and structure.
*
* @author Juan Carlos García
* @author Jose Manuel Vivó
* @since 2.0
*/
@Component
@Service
public class ThymeleafViewGeneratorServiceImpl extends
AbstractFreeMarkerViewGenerationService<Document, ThymeleafMetadata> implements
ThymeleafViewGeneratorService {
private static final String SPRING_ROO_THYMELEAF_TEMPLATES_LOCATION =
"spring.roo.thymeleaf.templates-location";
@Reference
private PathResolver pathResolver;
@Reference
private ProjectSettingsService projectSettings;
@Reference
private MemberDetailsScanner memberDetailsScanner;
@Override
public JavaType getType() {
return RooJavaType.ROO_THYMELEAF;
}
@Override
public String getName() {
return "THYMELEAF";
}
@Override
public String getViewsFolder(String moduleName) {
return pathResolver.getIdentifier(moduleName, Path.SRC_MAIN_RESOURCES, "/templates");
}
@Override
public String getViewsExtension() {
return ".html";
}
@Override
public String getLayoutsFolder(String moduleName) {
return getViewsFolder(moduleName).concat("/").concat("layouts");
}
@Override
public String getFragmentsFolder(String moduleName) {
return getViewsFolder(moduleName).concat("/").concat("fragments");
}
@Override
public Class<?> getResourceLoaderClass() {
return ThymeleafViewGeneratorServiceImpl.class;
}
@Override
public Document parse(String content) {
Document doc = Jsoup.parse(content, "", Parser.xmlParser());
doc.outputSettings().prettyPrint(false);
return doc;
}
/**
* Get ids and codes of old document that have 'user-managed' value in data-z attribute.
*
* @param loadExistingDoc
* @return
*/
private Map<String, String> mergeStructure(Document loadExistingDoc) {
Map<String, String> structureUserManaged = new HashMap<String, String>();
Elements elementsUserManaged =
loadExistingDoc.getElementsByAttributeValue("data-z", "user-managed");
List<Element> elementsToCheck = new ArrayList<Element>();
List<Element> topMostElements = new ArrayList<Element>();
// Get the top most user-managed level
Integer topMostElementLevel = null;
for (Element element : elementsUserManaged) {
if (topMostElementLevel == null) {
// Add first element always
topMostElementLevel = element.parents().size();
topMostElements.add(element);
continue;
}
if (element.parents().size() < topMostElementLevel) {
// Clear list and add the new element
topMostElementLevel = element.parents().size();
topMostElements.clear();
topMostElements.add(element);
continue;
}
if (element.parents().size() == topMostElementLevel) {
// Its a sibling element. Add the element to list without clearing it
topMostElements.add(element);
}
}
// First, add the top most elements
elementsToCheck.addAll(topMostElements);
for (Element element : elementsUserManaged) {
// Check if element is child any of the other elements added
boolean isChildElement = false;
for (Element topMostElement : topMostElements) {
if (StringUtils.isNotBlank(element.id())
&& topMostElement.getElementById(element.id()) != null) {
isChildElement = true;
break;
}
}
if (!isChildElement) {
// It is not child of top most level elements, so add it
elementsToCheck.add(element);
}
}
// Add elements to user managed structure
for (Element elementUserManaged : elementsToCheck) {
String id = elementUserManaged.attr("id");
if (StringUtils.isNotBlank(id)) {
String code = elementUserManaged.outerHtml();
structureUserManaged.put(id, code);
}
}
return structureUserManaged;
}
@Override
public Document merge(String templateName, Document loadExistingDoc,
ViewContext<ThymeleafMetadata> ctx, List<FieldItem> fields) {
for (FieldItem field : fields) {
// Get field code if data-z attribute value is equals to
// user-managed
Element elementField = loadExistingDoc.getElementById(field.getFieldId());
if (elementField != null && elementField.hasAttr("data-z")
&& elementField.attr("data-z").equals("user-managed")) {
field.setUserManaged(true);
field.setCodeManaged(elementField.outerHtml());
}
}
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
ctx.addExtraParameter("fields", fields);
Document newDoc = process(templateName, ctx);
return newDoc;
}
@Override
public Document mergeListView(String templateName, Document loadExistingDoc,
ViewContext<ThymeleafMetadata> ctx, EntityItem entity, List<FieldItem> fields,
List<List<DetailEntityItem>> detailsLevels) {
// Get field code if data-z attribute value is equals to user-managed
Element elementField =
loadExistingDoc.getElementById(entity.getEntityItemId().concat("-table"));
if (elementField != null && elementField.hasAttr("data-z")
&& elementField.attr("data-z").equals("user-managed")) {
entity.setUserManaged(true);
entity.setCodeManaged(elementField.outerHtml());
}
for (List<DetailEntityItem> detailsLevel : detailsLevels) {
for (DetailEntityItem detail : detailsLevel) {
// Get detail code if data-z attribute value is equals to
// user-managed
Element elementDetail =
loadExistingDoc.getElementById(detail.getEntityItemId().concat("-table"));
if (elementDetail != null && elementDetail.hasAttr("data-z")
&& elementDetail.attr("data-z").equals("user-managed")) {
detail.setUserManaged(true);
detail.setCodeManaged(elementDetail.outerHtml());
}
// Check tab code if data-z attribute value is equals to
// user-managed
Element elementTabCodeDetailField =
loadExistingDoc.getElementById(detail.getEntityItemId().concat("-table-tab"));
if (elementTabCodeDetailField != null && elementTabCodeDetailField.hasAttr("data-z")
&& elementTabCodeDetailField.attr("data-z").equals("user-managed")) {
detail.setTabLinkCode(elementTabCodeDetailField.outerHtml());
}
}
}
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
ctx.addExtraParameter("entity", entity);
ctx.addExtraParameter("fields", fields);
ctx.addExtraParameter("detailsLevels", detailsLevels);
Document newDoc = process(templateName, ctx);
return newDoc;
}
@Override
public Document mergeListDeleteModalView(String templateName, Document loadExistingDoc,
ViewContext<ThymeleafMetadata> ctx, EntityItem entity, List<FieldItem> fields) {
// Get field code if data-z attribute value is equals to user-managed
Element elementField =
loadExistingDoc.getElementById(entity.getEntityItemId().concat("-table"));
if (elementField != null && elementField.hasAttr("data-z")
&& elementField.attr("data-z").equals("user-managed")) {
entity.setUserManaged(true);
entity.setCodeManaged(elementField.outerHtml());
}
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
ctx.addExtraParameter("entity", entity);
ctx.addExtraParameter("fields", fields);
Document newDoc = process(templateName, ctx);
return newDoc;
}
@Override
public Document mergeListDeleteModalDetailView(String templateName, Document loadExistingDoc,
ViewContext<ThymeleafMetadata> ctx, EntityItem entity, List<FieldItem> fields) {
// Get field code if data-z attribute value is equals to user-managed
Element elementField =
loadExistingDoc.getElementById(entity.getEntityItemId().concat("-table"));
if (elementField != null && elementField.hasAttr("data-z")
&& elementField.attr("data-z").equals("user-managed")) {
entity.setUserManaged(true);
entity.setCodeManaged(elementField.outerHtml());
}
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
ctx.addExtraParameter("entity", entity);
ctx.addExtraParameter("fields", fields);
Document newDoc = process(templateName, ctx);
return newDoc;
}
@Override
public Document mergeListDeleteModalBatchView(String templateName, Document loadExistingDoc,
ViewContext<ThymeleafMetadata> ctx, EntityItem entity, List<FieldItem> fields) {
// Get field code if data-z attribute value is equals to user-managed
Element elementField =
loadExistingDoc.getElementById(entity.getEntityItemId().concat("-table"));
if (elementField != null && elementField.hasAttr("data-z")
&& elementField.attr("data-z").equals("user-managed")) {
entity.setUserManaged(true);
entity.setCodeManaged(elementField.outerHtml());
}
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
ctx.addExtraParameter("entity", entity);
ctx.addExtraParameter("fields", fields);
Document newDoc = process(templateName, ctx);
return newDoc;
}
@Override
public Document mergeListDeleteModalBatchDetailView(String templateName,
Document loadExistingDoc, ViewContext<ThymeleafMetadata> ctx, EntityItem entity,
List<FieldItem> fields) {
// Get field code if data-z attribute value is equals to user-managed
Element elementField =
loadExistingDoc.getElementById(entity.getEntityItemId().concat("-table"));
if (elementField != null && elementField.hasAttr("data-z")
&& elementField.attr("data-z").equals("user-managed")) {
entity.setUserManaged(true);
entity.setCodeManaged(elementField.outerHtml());
}
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
ctx.addExtraParameter("entity", entity);
ctx.addExtraParameter("fields", fields);
Document newDoc = process(templateName, ctx);
return newDoc;
}
@Override
public Document mergeMenu(String templateName, Document loadExistingDoc,
ViewContext<ThymeleafMetadata> ctx, List<MenuEntry> menuEntries) {
for (MenuEntry menuEntry : menuEntries) {
// Get field code if data-z attribute value is equals to
// user-managed
Element elementMenu = loadExistingDoc.getElementById(menuEntry.getId().concat("-entry"));
if (elementMenu != null && elementMenu.hasAttr("data-z")
&& elementMenu.attr("data-z").equals("user-managed")) {
menuEntry.setUserManaged(true);
menuEntry.setCodeManaged(elementMenu.outerHtml());
}
}
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
ctx.addExtraParameter("menuEntries", menuEntries);
Document newDoc = process(templateName, ctx);
return newDoc;
}
@Override
public Document merge(String templateName, Document loadExistingDoc,
ViewContext<ThymeleafMetadata> ctx) {
// TODO: TO BE FIXED WITH NEW COMMAND 'web mvc view update'
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
Document newDoc = process(templateName, ctx);
return newDoc;
}
@Override
public String getTemplatesLocation() {
String thymeleafTemplatesLocation =
projectSettings.getProperty(SPRING_ROO_THYMELEAF_TEMPLATES_LOCATION);
if (thymeleafTemplatesLocation != null) {
return pathResolver.getIdentifier("", Path.ROOT_ROO_CONFIG, thymeleafTemplatesLocation);
}
return pathResolver.getIdentifier("", Path.ROOT_ROO_CONFIG, "templates/thymeleaf/default");
}
@Override
public void writeDoc(Document document, String viewPath) {
// Write doc on disk
if (document != null && StringUtils.isNotBlank(viewPath)) {
getFileManager().createOrUpdateTextFileIfRequired(viewPath, document.html(), false);
}
}
@Override
public void installTemplates() {
// Getting destination where FreeMarker templates should be installed.
// This will allow developers to customize his own templates.
copyDirectoryContents("templates/*.ftl", getTemplatesLocation(), true);
copyDirectoryContents("templates/fragments/*.ftl",
getTemplatesLocation().concat("/").concat("fragments"), true);
copyDirectoryContents("templates/layouts/*.ftl",
getTemplatesLocation().concat("/").concat("layouts"), true);
copyDirectoryContents("templates/fields/*.ftl",
getTemplatesLocation().concat("/").concat("fields"), true);
}
@Override
public void addModalConfirmDelete(String moduleName, ViewContext<ThymeleafMetadata> ctx) {
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getFragmentsFolder(moduleName).concat("/modal-confirm-delete").concat(getViewsExtension());
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc = merge("fragments/modal-confirm-delete", loadExistingDoc(viewName), ctx);
} else {
newDoc = process("fragments/modal-confirm-delete", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
@Override
public void addModalConfirmDeleteBatch(String moduleName, ViewContext<ThymeleafMetadata> ctx) {
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getFragmentsFolder(moduleName).concat("/modal-confirm-delete-batch").concat(
getViewsExtension());
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc = merge("fragments/modal-confirm-delete-batch", loadExistingDoc(viewName), ctx);
} else {
newDoc = process("fragments/modal-confirm-delete-batch", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
@Override
public void addModalExportEmptyError(String moduleName, ViewContext<ThymeleafMetadata> ctx) {
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getFragmentsFolder(moduleName).concat("/modal-export-empty-error").concat(
getViewsExtension());
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc = merge("fragments/modal-export-empty-error", loadExistingDoc(viewName), ctx);
} else {
newDoc = process("fragments/modal-export-empty-error", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
@Override
protected DetailEntityItem createDetailEntityItem(ThymeleafMetadata detailController,
MemberDetails entityMembers, JpaEntityMetadata entityMetadata, String entityName,
ViewContext<ThymeleafMetadata> ctx, String detailSuffix, EntityItem rootEntity) {
JavaType detailItemController;
JavaType detailCollectionController;
final ControllerType type = detailController.getControllerMetadata().getType();
if (type == ControllerType.DETAIL) {
detailCollectionController = detailController.getDestination();
detailItemController = detailController.getDetailItemController();
} else {
detailCollectionController = detailController.getDetailCollectionController();
detailItemController = detailController.getDestination();
}
JavaType itemController = detailController.getItemController();
JavaType relatedCollectionController = detailController.getRelatedCollectionController();
JavaType relatedItemController = detailController.getRelatedItemController();
DetailEntityItem item =
super.createDetailEntityItem(detailController, entityMembers, entityMetadata, entityName,
ctx, detailSuffix, rootEntity);
item.addConfigurationElement("mvcDetailControllerName", detailController.getMvcControllerName());
item.addConfigurationElement("mvcDetailItemControllerName",
detailItemController.getSimpleTypeName());
item.addConfigurationElement("mvcDetailCollectionControllerName",
detailCollectionController.getSimpleTypeName());
if (relatedItemController != null) {
item.addConfigurationElement("select2MethodName", ThymeleafMetadata.SELECT2_METHOD_NAME);
item.addConfigurationElement("select2ControllerName",
relatedCollectionController.getSimpleTypeName());
}
return item;
}
@Override
public Map<String, String> getI18nLabels(MemberDetails entityMemberDetails, JavaType entity,
JpaEntityMetadata entityMetadata, ControllerMetadata controllerMetadata, String module,
ViewContext<ThymeleafMetadata> ctx) {
Map<String, String> labels =
super.getI18nLabels(entityMemberDetails, entity, entityMetadata, controllerMetadata,
module, ctx);
if (controllerMetadata != null && controllerMetadata.getType() == ControllerType.DETAIL) {
String key = getCreateDetailsSelect2PlaceholderLabelKey(controllerMetadata, ctx);
String value = "Select " + controllerMetadata.getLastDetailsInfo().fieldName + " to add";
labels.put(key, value);
}
return labels;
}
private String getCreateDetailsSelect2PlaceholderLabelKey(ControllerMetadata controllerMetadata,
ViewContext<ThymeleafMetadata> ctx) {
return "info_select_" + controllerMetadata.getDetailsPathAsString("_");
}
@Override
public void addDetailsViews(String moduleName, JpaEntityMetadata entityMetadata,
MemberDetails entity, ControllerMetadata controllerMetadata, ThymeleafMetadata viewMetadata,
ViewContext<ThymeleafMetadata> ctx) {
super
.addDetailsViews(moduleName, entityMetadata, entity, controllerMetadata, viewMetadata, ctx);
if (controllerMetadata.getLastDetailsInfo().type == JpaRelationType.AGGREGATION) {
addCreateDetailsView(moduleName, entityMetadata, entity, controllerMetadata, viewMetadata,
ctx);
} else {
addCreateDetailsCompositionView(moduleName, entityMetadata, entity, controllerMetadata,
viewMetadata, ctx);
}
addListDeleteModalDetailView(moduleName, entityMetadata, entity, controllerMetadata, ctx);
addListDeleteModalDetailBatchView(moduleName, entityMetadata, entity, controllerMetadata, ctx);
}
@Override
public void addDetailsItemViews(String moduleName, JpaEntityMetadata entityMetadata,
MemberDetails entity, ControllerMetadata controllerMetadata, ThymeleafMetadata viewMetadata,
ViewContext<ThymeleafMetadata> ctx) {
super.addDetailsItemViews(moduleName, entityMetadata, entity, controllerMetadata, viewMetadata,
ctx);
// Get root entity metadata
EntityItem entityItem = createEntityItem(entityMetadata, ctx, TABLE_SUFFIX);
DetailEntityItem detail =
createDetailEntityItem(viewMetadata, entity, entityMetadata, ctx.getEntityName(), ctx,
DETAIL_SUFFIX, entityItem);
detail.addConfigurationElement("entityLabel",
StringUtils.uncapitalize(FieldItem.buildLabel(detail.getEntityName(), "")));
ViewContext<ThymeleafMetadata> childCtx =
createViewContext(controllerMetadata, controllerMetadata.getLastDetailEntity(),
controllerMetadata.getLastDetailsInfo().childEntityMetadata, viewMetadata);
// TODO
addShowDetailsCompositionView(moduleName, entityMetadata, viewMetadata, entityItem, detail,
childCtx);
addUpdateDetailsCompositionView(moduleName, entityMetadata, viewMetadata, entityItem, detail,
childCtx);
addListDeleteModalDetailView(moduleName, entityMetadata, entity, controllerMetadata, ctx);
addListDeleteModalDetailBatchView(moduleName, entityMetadata, entity, controllerMetadata, ctx);
}
@Override
public void addFinderFormView(String moduleName, JpaEntityMetadata entityMetadata,
ThymeleafMetadata viewMetadata, JavaType formBean, String finderName,
ViewContext<ThymeleafMetadata> ctx) {
// Getting formBean details
MemberDetails formBeanDetails =
getMemberDetailsScanner().getMemberDetails(getClass().getName(),
getTypeLocationService().getTypeDetails(formBean));
// Getting entity fields that should be included on view
List<FieldMetadata> formBeanFields = getPersistentFields(formBeanDetails.getFields());
List<FieldItem> fields =
getFieldViewItems(entityMetadata, formBeanFields, ctx.getEntityName(), true, ctx,
TABLE_SUFFIX);
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getViewsFolder(moduleName).concat(ctx.getControllerPath()).concat("/").concat(finderName)
.concat("Form").concat(getViewsExtension());
EntityItem entityItem = createEntityItem(entityMetadata, ctx, TABLE_SUFFIX);
ctx.addExtraParameter("finderName", finderName.replace("findBy", "by"));
ctx.addExtraParameter("entity", entityItem);
ctx.addExtraParameter("fields", fields);
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc =
mergeListView("finderForm", loadExistingDoc(viewName), ctx, entityItem, fields,
new ArrayList<List<DetailEntityItem>>());
} else {
newDoc = process("finderForm", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
@Override
public void addFinderListView(String moduleName, JpaEntityMetadata entityMetadata,
MemberDetails entity, ThymeleafMetadata viewMetadata, JavaType formBean, JavaType returnType,
String finderName, List<ThymeleafMetadata> detailsControllers,
ViewContext<ThymeleafMetadata> ctx) {
// Getting returnType details
MemberDetails returnTypeDetails =
getMemberDetailsScanner().getMemberDetails(getClass().getName(),
getTypeLocationService().getTypeDetails(returnType));
// Getting entity fields that should be included on view
List<FieldMetadata> returnFields = getPersistentFields(returnTypeDetails.getFields());
List<FieldItem> fields =
getFieldViewItems(entityMetadata, returnFields, ctx.getEntityName(), true, ctx,
TABLE_SUFFIX);
// Getting formBean details
MemberDetails formBeanDetails =
getMemberDetailsScanner().getMemberDetails(getClass().getName(),
getTypeLocationService().getTypeDetails(formBean));
// Getting entity fields that should be included on view
List<FieldMetadata> formBeanFieldsMetadata = getPersistentFields(formBeanDetails.getFields());
List<FieldItem> formBeanFields =
getFieldViewItems(entityMetadata, formBeanFieldsMetadata, ctx.getEntityName(), true, ctx,
TABLE_SUFFIX);
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getViewsFolder(moduleName).concat(ctx.getControllerPath()).concat("/").concat(finderName)
.concat(getViewsExtension());
EntityItem entityItem = createEntityItem(entityMetadata, ctx, TABLE_SUFFIX);
List<List<DetailEntityItem>> detailsLevels = new ArrayList<List<DetailEntityItem>>();
if (detailsControllers != null && !detailsControllers.isEmpty()) {
List<DetailEntityItem> details = new ArrayList<DetailEntityItem>();
for (ThymeleafMetadata detailController : detailsControllers) {
DetailEntityItem detailItem =
createDetailEntityItem(detailController, entity, entityMetadata, ctx.getEntityName(),
ctx, DETAIL_SUFFIX, entityItem);
details.add(detailItem);
}
// Sort details by path
Collections.sort(details, new Comparator<DetailEntityItem>() {
@Override
public int compare(DetailEntityItem o1, DetailEntityItem o2) {
return o1.getPathString().compareTo(o2.getPathString());
}
});
// Locates parent details for children, grandsons, etc and make groups by
// levels
for (DetailEntityItem detail : details) {
// Create group until item level
while (detailsLevels.size() < detail.getLevel()) {
detailsLevels.add(new ArrayList<DetailEntityItem>());
}
// Include detail in its group
detailsLevels.get(detail.getLevel() - 1).add(detail);
if (detail.getLevel() < 1) {
// Nothing more to do with detail
continue;
}
// look for parent
for (DetailEntityItem parent : details) {
if (detail.isTheParentEntity(parent)) {
// set parent
detail.setParentEntity(parent);
break;
}
}
}
}
ctx.addExtraParameter("detailsLevels", detailsLevels);
final JavaType searchController = viewMetadata.getDestination();
Map<String, MethodMetadata> finderDatatablesMethods = viewMetadata.getFinderDatatableMethods();
MethodMetadata finderDtMethod = finderDatatablesMethods.get(finderName);
Map<String, MethodMetadata> finderListMethods = viewMetadata.getFinderListMethods();
MethodMetadata finderListMethod = finderListMethods.get(finderName);
Map<String, MethodMetadata> finderFormMethods = viewMetadata.getFinderFormMethods();
MethodMetadata finderFormMethod = finderFormMethods.get(finderName);
ctx.addExtraParameter("entity", entityItem);
ctx.addExtraParameter("fields", fields);
// Adding formBean fields
ctx.addExtraParameter("formbeanfields", formBeanFields);
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc =
mergeListView("finderList", loadExistingDoc(viewName), ctx, entityItem, fields,
detailsLevels);
} else {
newDoc = process("finderList", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
private void addShowDetailsCompositionView(String moduleName, JpaEntityMetadata entityMetadata,
ThymeleafMetadata viewMetadata, EntityItem entityItem, DetailEntityItem detail,
ViewContext<ThymeleafMetadata> ctx) {
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getViewsFolder(moduleName).concat(ctx.getControllerPath()).concat("/")
.concat(viewMetadata.getControllerMetadata().getDetailsPathAsString("/"))
.concat("/show").concat(getViewsExtension());
ctx.addExtraParameter("entity", entityItem);
ctx.addExtraParameter("detail", detail);
// TODO
ctx.addExtraParameter("details", Collections.EMPTY_LIST);
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc =
mergeDetailsCompositionView("showDetailComposition", loadExistingDoc(viewName), ctx,
entityItem, detail, (List<FieldItem>) detail.getConfiguration().get("fields"));
} else {
ctx.addExtraParameter("fields", detail.getConfiguration().get("fields"));
newDoc = process("showDetailComposition", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
private void addUpdateDetailsCompositionView(String moduleName, JpaEntityMetadata entityMetadata,
ThymeleafMetadata viewMetadata, EntityItem entityItem, DetailEntityItem detail,
ViewContext<ThymeleafMetadata> ctx) {
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getViewsFolder(moduleName).concat(ctx.getControllerPath()).concat("/")
.concat(viewMetadata.getControllerMetadata().getDetailsPathAsString("/"))
.concat("/edit").concat(getViewsExtension());
ctx.addExtraParameter("entity", entityItem);
ctx.addExtraParameter("detail", detail);
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc =
mergeDetailsCompositionView("editDetailComposition", loadExistingDoc(viewName), ctx,
entityItem, detail, (List<FieldItem>) detail.getConfiguration().get("fields"));
} else {
ctx.addExtraParameter("fields", detail.getConfiguration().get("fields"));
newDoc = process("editDetailComposition", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
protected void addCreateDetailsView(String moduleName, JpaEntityMetadata entityMetadata,
MemberDetails entity, ControllerMetadata controllerMetadata, ThymeleafMetadata viewMetadata,
ViewContext<ThymeleafMetadata> ctx) {
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getViewsFolder(moduleName).concat(ctx.getControllerPath()).concat("/")
.concat(controllerMetadata.getDetailsPathAsString("/")).concat("/create")
.concat(getViewsExtension());
// Get root entity metadata
EntityItem entityItem = createEntityItem(entityMetadata, ctx, TABLE_SUFFIX);
DetailEntityItem detail =
createDetailEntityItem(viewMetadata, entity, entityMetadata, ctx.getEntityName(), ctx,
DETAIL_SUFFIX, entityItem);
detail.addConfigurationElement("entityLabel",
StringUtils.uncapitalize(FieldItem.buildLabel(detail.getEntityName(), "")));
ctx.addExtraParameter("entity", entityItem);
ctx.addExtraParameter("detail", detail);
ctx.addExtraParameter("select2_placeholder",
getCreateDetailsSelect2PlaceholderLabelKey(controllerMetadata, ctx));
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc =
mergeCreateDetailsView("createDetail", loadExistingDoc(viewName), ctx, entityItem, detail);
} else {
newDoc = process("createDetail", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
protected void addCreateDetailsCompositionView(String moduleName,
JpaEntityMetadata entityMetadata, MemberDetails entity,
ControllerMetadata controllerMetadata, ThymeleafMetadata viewMetadata,
ViewContext<ThymeleafMetadata> ctx) {
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getViewsFolder(moduleName).concat(ctx.getControllerPath()).concat("/")
.concat(controllerMetadata.getDetailsPathAsString("/")).concat("/create")
.concat(getViewsExtension());
// Get root entity metadata
EntityItem entityItem = createEntityItem(entityMetadata, ctx, TABLE_SUFFIX);
DetailEntityItem detail =
createDetailEntityItem(viewMetadata, entity, entityMetadata, ctx.getEntityName(), ctx,
DETAIL_SUFFIX, entityItem);
ViewContext<ThymeleafMetadata> childCtx =
createViewContext(controllerMetadata, controllerMetadata.getLastDetailEntity(),
controllerMetadata.getLastDetailsInfo().childEntityMetadata, viewMetadata);
detail.addConfigurationElement("entityLabel",
StringUtils.uncapitalize(FieldItem.buildLabel(detail.getEntityName(), "")));
childCtx.addExtraParameter("entity", entityItem);
childCtx.addExtraParameter("detail", detail);
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc =
mergeDetailsCompositionView("createDetailComposition", loadExistingDoc(viewName),
childCtx, entityItem, detail,
(List<FieldItem>) detail.getConfiguration().get("fields"));
} else {
childCtx.addExtraParameter("fields", detail.getConfiguration().get("fields"));
newDoc = process("createDetailComposition", childCtx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
private Document mergeDetailsCompositionView(String templateName, Document loadExistingDoc,
ViewContext ctx, EntityItem entityItem, DetailEntityItem detail, List<FieldItem> fields) {
for (FieldItem field : fields) {
// Get field code if data-z attribute value is equals to
// user-managed
Element elementField = loadExistingDoc.getElementById(field.getFieldId());
if (elementField != null && elementField.hasAttr("data-z")
&& elementField.attr("data-z").equals("user-managed")) {
field.setUserManaged(true);
field.setCodeManaged(elementField.outerHtml());
} else {
field.setUserManaged(false);
field.setCodeManaged("");
}
}
ctx.addExtraParameter("fields", fields);
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
return process(templateName, ctx);
}
private Document mergeCreateDetailsView(String templateName, Document loadExistingDoc,
ViewContext ctx, EntityItem entityItem, DetailEntityItem detail) {
Element elementDetail = loadExistingDoc.getElementById(detail.getEntityItemId());
if (elementDetail != null && elementDetail.hasAttr("data-z")
&& elementDetail.attr("data-z").equals("user-managed")) {
detail.setUserManaged(true);
detail.setCodeManaged(elementDetail.outerHtml());
ctx.addExtraParameter("detail", detail);
}
ctx.addExtraParameter("userManagedComponents", mergeStructure(loadExistingDoc));
return process(templateName, ctx);
}
@Override
public ViewContext<ThymeleafMetadata> createViewContext(
final ControllerMetadata controllerMetadata, final JavaType entity,
final JpaEntityMetadata entityMetadata, ThymeleafMetadata metadata) {
ViewContext<ThymeleafMetadata> ctx =
super.createViewContext(controllerMetadata, entity, entityMetadata, metadata);
final JavaType itemCtrl;
final JavaType collCtrl;
final JavaType searchCtrl;
ctx.addExtraParameter("mvcControllerName", metadata.getMvcControllerName());
if (controllerMetadata.getType() == ControllerType.COLLECTION) {
collCtrl = controllerMetadata.getDestination();
} else {
collCtrl = metadata.getCollectionController();
}
ctx.addExtraParameter("mvcCollectionControllerName",
ThymeleafMetadata.getMvcControllerName(collCtrl));
if (controllerMetadata.getType() == ControllerType.ITEM) {
itemCtrl = controllerMetadata.getDestination();
} else {
itemCtrl = metadata.getItemController();
}
ctx.addExtraParameter("mvcItemControllerName", ThymeleafMetadata.getMvcControllerName(itemCtrl));
// Adding search linkfactory if needed
if (controllerMetadata.getType() == ControllerType.SEARCH) {
searchCtrl = controllerMetadata.getDestination();
} else {
searchCtrl = null;
}
if (searchCtrl != null) {
ctx.addExtraParameter("mvcSearchControllerName",
ThymeleafMetadata.getMvcControllerName(searchCtrl));
}
return ctx;
}
@Override
protected boolean getReferenceField(FieldItem fieldItem, ClassOrInterfaceTypeDetails typeDetails,
ViewContext<ThymeleafMetadata> ctx) {
boolean found = super.getReferenceField(fieldItem, typeDetails, ctx);
if (!found) {
return false;
}
ClassOrInterfaceTypeDetails referenceController =
(ClassOrInterfaceTypeDetails) fieldItem.getConfiguration().get("referencedController");
fieldItem.addConfigurationElement("select2MethodName", ThymeleafMetadata.SELECT2_METHOD_NAME);
fieldItem.addConfigurationElement("select2ControllerName", referenceController.getType()
.getSimpleTypeName());
return true;
}
@Override
public void addDefaultListLayout(String moduleName, ViewContext<ThymeleafMetadata> ctx) {
// Process elements to generate
Document newDoc = null;
// Getting new viewName
String viewName =
getLayoutsFolder(moduleName).concat("/default-list-layout").concat(getViewsExtension());
// Check if new view to generate exists or not
if (existsFile(viewName)) {
newDoc = merge("layouts/default-list-layout", loadExistingDoc(viewName), ctx);
} else {
newDoc = process("layouts/default-list-layout", ctx);
}
// Write newDoc on disk
writeDoc(newDoc, viewName);
}
}