/*
* Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
*
* This library 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; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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.
*/
package com.agiletec.apsadmin.system;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.agiletec.aps.system.common.tree.ITreeNode;
import com.agiletec.aps.system.common.tree.TreeNode;
import com.agiletec.aps.system.exception.ApsSystemException;
/**
* Classe base per gli helper che gestiscono le operazioni su oggetti alberi.
* @author E.Santoboni
*/
public abstract class TreeNodeBaseActionHelper extends BaseActionHelper implements ITreeNodeBaseActionHelper {
private static final Logger _logger = LoggerFactory.getLogger(TreeNodeBaseActionHelper.class);
/**
* Costruisce il codice univoco di un nodo in base ai parametri specificato.
* Il metodo:
* 1) elimina i caratteri non compresi tra "a" e "z", tra "0" e "9".
* 2) taglia (se necessario) la stringa secondo la lunghezza massima immessa.
* 3) verifica se esistono entità con il codice ricavato (ed in tal caso appende il suffisso "_<numero>" fino a che non trova un codice univoco).
* @param title Il titolo del nuovo nodo.
* @param baseDefaultCode Un codice nodo di default.
* @param maxLength La lunghezza massima del codice.
* @return Il codice univoco univoco ricavato.
* @throws ApsSystemException In caso di errore.
*/
@Override
public String buildCode(String title, String baseDefaultCode, int maxLength) throws ApsSystemException {
String uniqueCode = null;
try {
// punto 1
uniqueCode = (null != title) ? purgeString(title) : baseDefaultCode;
if (uniqueCode.length() == 0) {
uniqueCode = baseDefaultCode;
}
// punto 2
if (uniqueCode.length() > maxLength) {
uniqueCode = uniqueCode.substring(0, maxLength);
if (uniqueCode.charAt(uniqueCode.length()-1) == '_') {
uniqueCode = uniqueCode.substring(0, uniqueCode.length()-1);
}
}
//punto 3
if (null != this.getTreeNode(uniqueCode)) {
int index = 0;
String currentCode = null;
do {
index++;
currentCode = uniqueCode + "_" + index;
} while (null != this.getTreeNode(currentCode));
uniqueCode = currentCode;
}
} catch (Throwable t) {
throw new ApsSystemException("Errore in creazione nuovo codice", t);
}
return uniqueCode;
}
@Override
public Set<String> checkTargetNodes(String nodeToOpen, Set<String> lastOpenedNodes, Collection<String> groupCodes) throws ApsSystemException {
Set<String> checkedTargetNodes = new HashSet<String>();
try {
if (null != nodeToOpen && this.checkNode(nodeToOpen, groupCodes)) {
checkedTargetNodes.add(nodeToOpen);
}
if (null != lastOpenedNodes) {
Iterator<String> iter = lastOpenedNodes.iterator();
while (iter.hasNext()) {
String code = (String) iter.next();
if (null != code && this.checkNode(code, groupCodes)) {
checkedTargetNodes.add(code);
}
}
}
} catch (Throwable t) {
_logger.error("Error check target nodes", t);
throw new ApsSystemException("Error check target nodes", t);
}
return checkedTargetNodes;
}
@Override
public Set<String> checkTargetNodesOnClosing(String nodeToCloseCode, Set<String> lastOpenedNodes, Collection<String> groupCodes) throws ApsSystemException {
ITreeNode nodeToClose = this.getTreeNode(nodeToCloseCode);
if (null == nodeToCloseCode || null == nodeToClose) {
return this.checkTargetNodes(null, lastOpenedNodes, groupCodes);
}
Set<String> checkedTargetNodes = new HashSet<String>();
try {
if (nodeToClose.isRoot()) {
return checkedTargetNodes;
}
if (null != lastOpenedNodes) {
Iterator<String> iter = lastOpenedNodes.iterator();
while (iter.hasNext()) {
String code = (String) iter.next();
if (null != code && this.checkNode(code, groupCodes)
&& !code.equals(nodeToCloseCode) && !this.getTreeNode(code).isChildOf(nodeToCloseCode)) {
checkedTargetNodes.add(code);
}
}
}
if (null != nodeToClose.getParent()
&& this.checkNode(nodeToClose.getParent().getCode(), groupCodes)) {
checkedTargetNodes.add(nodeToClose.getParent().getCode());
}
} catch (Throwable t) {
_logger.error("Error check target nodes on closing tree", t);
throw new ApsSystemException("Error check target nodes on closing tree", t);
}
return checkedTargetNodes;
}
protected boolean checkNode(String nodeCode, Collection<String> groupCodes) {
if (!this.isNodeAllowed(nodeCode, groupCodes)) {
_logger.error("Node '{}' not allowed ", nodeCode);
return false;
}
ITreeNode treeNode = this.getTreeNode(nodeCode);
if (null == treeNode) {
_logger.error("Node '{}' null", nodeCode);
return false;
}
return true;
}
@Override
public TreeNodeWrapper getShowableTree(Set<String> treeNodesToOpen, ITreeNode fullTree, Collection<String> groupCodes) throws ApsSystemException {
if (null == treeNodesToOpen || treeNodesToOpen.isEmpty()) {
_logger.warn("No selected nodes");
return new TreeNodeWrapper(fullTree);
}
TreeNodeWrapper root = null;
try {
Set<String> checkNodes = new HashSet<String>();
this.buildCheckNodes(treeNodesToOpen, checkNodes, groupCodes);
root = new TreeNodeWrapper(fullTree);
root.setParent(root);
this.builShowableTree(root, null, fullTree, checkNodes);
} catch (Throwable t) {
_logger.error("Error creating showable tree", t);
throw new ApsSystemException("Error creating showable tree", t);
}
return root;
}
private void buildCheckNodes(Set<String> treeNodesToOpen, Set<String> checkNodes, Collection<String> groupCodes) {
if (null == treeNodesToOpen) return;
Iterator<String> iter = treeNodesToOpen.iterator();
while (iter.hasNext()) {
String targetNode = (String) iter.next();
ITreeNode treeNode = this.getTreeNode(targetNode);
if (null != treeNode) {
this.buildCheckNodes(treeNode, checkNodes, groupCodes);
}
}
}
private void builShowableTree(TreeNodeWrapper currentNode, TreeNodeWrapper parent, ITreeNode currentTreeNode, Set<String> checkNodes) {
if (checkNodes.contains(currentNode.getCode())) {
currentNode.setOpen(true);
ITreeNode[] children = currentTreeNode.getChildren();
for (int i=0; i<children.length; i++) {
ITreeNode newCurrentTreeNode = children[i];
TreeNodeWrapper newNode = new TreeNodeWrapper(newCurrentTreeNode);
newNode.setParent(currentNode);
currentNode.addChild(newNode);
this.builShowableTree(newNode, currentNode, newCurrentTreeNode, checkNodes);
}
}
}
protected void buildCheckNodes(ITreeNode treeNode, Set<String> checkNodes, Collection<String> groupCodes) {
checkNodes.add(treeNode.getCode());
ITreeNode parent = treeNode.getParent();
if (parent != null && parent.getParent() != null &&
!parent.getCode().equals(treeNode.getCode())) {
this.buildCheckNodes(parent, checkNodes, groupCodes);
}
}
protected abstract boolean isNodeAllowed(String code, Collection<String> groupCodes);
/**
* Default implementation of the method.
* Build a root node cloning the returned tree from the helper.
* @param groupCodes the groups codes
* @return the root node
* @throws ApsSystemException in caso of error
*/
@Override
public ITreeNode getAllowedTreeRoot(Collection<String> groupCodes) throws ApsSystemException {
TreeNode root = new TreeNode();
ITreeNode currentRoot = this.getRoot();
this.fillTreeNode(root, root, currentRoot);
this.addTreeWrapper(root, null, currentRoot);
return root;
}
private void addTreeWrapper(TreeNode currentNode, TreeNode parent, ITreeNode currentTreeNode) {
ITreeNode[] children = currentTreeNode.getChildren();
for (int i=0; i<children.length; i++) {
ITreeNode newCurrentTreeNode = children[i];
TreeNode newNode = new TreeNode();
this.fillTreeNode(newNode, currentNode, newCurrentTreeNode);
currentNode.addChild(newNode);
this.addTreeWrapper(newNode, currentNode, newCurrentTreeNode);
}
}
/**
* Valorizza un nodo in base alle informazioni specificate..
* @param nodeToValue Il nodo da valorizzare.
* @param parent Il nodo parente.
* @param realNode Il nodo dal quela estrarre le info.
*/
protected void fillTreeNode(TreeNode nodeToValue, TreeNode parent, ITreeNode realNode) {
nodeToValue.setCode(realNode.getCode());
if (null == parent) {
nodeToValue.setParent(nodeToValue);
} else {
nodeToValue.setParent(parent);
}
Set<Object> codes = realNode.getTitles().keySet();
Iterator<Object> iterKey = codes.iterator();
while (iterKey.hasNext()) {
String key = (String) iterKey.next();
String title = realNode.getTitles().getProperty(key);
nodeToValue.getTitles().put(key, title);
}
}
/**
* Return the root node of the managed tree.
* @return The root node.
*/
protected abstract ITreeNode getRoot();
/**
* Return a node of the managed tree.
* @param code The code of the node to return.
* @return The required node.
*/
protected abstract ITreeNode getTreeNode(String code);
}