org.nuclos.server.navigation.ejb3.TreeNodeFacadeBean.java Source code

Java tutorial

Introduction

Here is the source code for org.nuclos.server.navigation.ejb3.TreeNodeFacadeBean.java

Source

//Copyright (C) 2010  Novabit Informationssysteme GmbH
//
//This file is part of Nuclos.
//
//Nuclos 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.
//
//Nuclos 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 Affero General Public License for more details.
//
//You should have received a copy of the GNU Affero General Public License
//along with Nuclos.  If not, see <http://www.gnu.org/licenses/>.
package org.nuclos.server.navigation.ejb3;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableMap;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.TreeMap;

import javax.annotation.security.RolesAllowed;

import org.nuclos.common.AttributeProvider;
import org.nuclos.common.EntityTreeViewVO;
import org.nuclos.common.NuclosBusinessException;
import org.nuclos.common.NuclosEOField;
import org.nuclos.common.NuclosEntity;
import org.nuclos.common.NuclosFatalException;
import org.nuclos.common.ParameterProvider;
import org.nuclos.common.SearchConditionUtils;
import org.nuclos.common.collect.collectable.searchcondition.CollectableComparison;
import org.nuclos.common.collect.collectable.searchcondition.CollectableSearchCondition;
import org.nuclos.common.collect.collectable.searchcondition.ComparisonOperator;
import org.nuclos.common.collection.CollectionUtils;
import org.nuclos.common.collection.Transformer;
import org.nuclos.common.dal.vo.EntityFieldMetaDataVO;
import org.nuclos.common.dal.vo.EntityMetaDataVO;
import org.nuclos.common.dal.vo.EntityObjectVO;
import org.nuclos.common.dal.vo.SystemFields;
import org.nuclos.common.dblayer.JoinType;
import org.nuclos.common.security.Permission;
import org.nuclos.common2.SpringLocaleDelegate;
import org.nuclos.common2.IdUtils;
import org.nuclos.common2.LangUtils;
import org.nuclos.common2.StringUtils;
import org.nuclos.common2.TruncatableCollection;
import org.nuclos.common2.exception.CommonFatalException;
import org.nuclos.common2.exception.CommonFinderException;
import org.nuclos.common2.exception.CommonPermissionException;
import org.nuclos.server.common.AttributeCache;
import org.nuclos.server.common.DatasourceCache;
import org.nuclos.server.common.MasterDataMetaCache;
import org.nuclos.server.common.MetaDataServerProvider;
import org.nuclos.server.common.RuleCache;
import org.nuclos.server.common.SecurityCache;
import org.nuclos.server.common.ServerParameterProvider;
import org.nuclos.server.common.ejb3.LocaleFacadeLocal;
import org.nuclos.server.common.ejb3.NuclosFacadeBean;
import org.nuclos.server.dal.DalUtils;
import org.nuclos.server.dal.processor.nuclet.JdbcEntityObjectProcessor;
import org.nuclos.server.dal.provider.NucletDalProvider;
import org.nuclos.server.dblayer.DbInvalidResultSizeException;
import org.nuclos.server.dblayer.DbTuple;
import org.nuclos.server.dblayer.query.DbColumnExpression;
import org.nuclos.server.dblayer.query.DbFrom;
import org.nuclos.server.dblayer.query.DbQuery;
import org.nuclos.server.dblayer.query.DbQueryBuilder;
import org.nuclos.server.genericobject.Modules;
import org.nuclos.server.genericobject.ejb3.GenericObjectFacadeLocal;
import org.nuclos.server.genericobject.searchcondition.CollectableSearchExpression;
import org.nuclos.server.genericobject.valueobject.GenericObjectWithDependantsVO;
import org.nuclos.server.masterdata.ejb3.MasterDataFacadeLocal;
import org.nuclos.server.masterdata.valueobject.MasterDataMetaVO;
import org.nuclos.server.masterdata.valueobject.MasterDataVO;
import org.nuclos.server.navigation.treenode.DefaultMasterDataTreeNode;
import org.nuclos.server.navigation.treenode.DynamicTreeNode;
import org.nuclos.server.navigation.treenode.EntitySearchResultTreeNode;
import org.nuclos.server.navigation.treenode.GenericObjectTreeNode;
import org.nuclos.server.navigation.treenode.GenericObjectTreeNode.RelationDirection;
import org.nuclos.server.navigation.treenode.GenericObjectTreeNode.SystemRelationType;
import org.nuclos.server.navigation.treenode.GenericObjectTreeNodeFactory;
import org.nuclos.server.navigation.treenode.GroupSearchResultTreeNode;
import org.nuclos.server.navigation.treenode.GroupTreeNode;
import org.nuclos.server.navigation.treenode.MasterDataSearchResultTreeNode;
import org.nuclos.server.navigation.treenode.MasterDataTreeNode;
import org.nuclos.server.navigation.treenode.RelationTreeNode;
import org.nuclos.server.navigation.treenode.SubFormEntryTreeNode;
import org.nuclos.server.navigation.treenode.SubFormTreeNode;
import org.nuclos.server.navigation.treenode.TreeNode;
import org.nuclos.server.navigation.treenode.nuclet.NucletTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.NuclosInstanceTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.content.AbstractNucletContentEntryTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.content.DefaultNucletContentEntryTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.content.NucletContentCustomComponentTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.content.NucletContentEntityTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.content.NucletContentProcessTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.content.NucletContentRuleTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.content.NucletContentTreeNode;
import org.nuclos.server.navigation.treenode.nuclet.content.ReportNucletContentTreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

/**
 * Facade bean for managing explorer tree structures and contents.
 * <br>
 * <br>Created by Novabit Informationssysteme GmbH
 * <br>Please visit <a href="http://www.novabit.de">www.novabit.de</a>
 *
 * @todo restrict
*/
@Transactional(noRollbackFor = { Exception.class })
@RolesAllowed("Login")
public class TreeNodeFacadeBean extends NuclosFacadeBean implements TreeNodeFacadeRemote {

    private static final int DEFAULT_ROWCOUNT_FOR_SEARCHRESULT = 500;

    //

    private ServerParameterProvider serverParameterProvider;

    private GenericObjectFacadeLocal genericObjectFacade;

    private MasterDataFacadeLocal masterDataFacade;

    private LocaleFacadeLocal localeFacade;

    public TreeNodeFacadeBean() {
    }

    @Autowired
    void setServerParameterProvider(ServerParameterProvider serverParameterProvider) {
        this.serverParameterProvider = serverParameterProvider;
    }

    @Autowired
    final void setGenericObjectFacade(GenericObjectFacadeLocal genericObjectFacade) {
        this.genericObjectFacade = genericObjectFacade;
    }

    private final GenericObjectFacadeLocal getGenericObjectFacade() {
        return genericObjectFacade;
    }

    @Autowired
    final void setMasterDataFacade(MasterDataFacadeLocal masterDataFacade) {
        this.masterDataFacade = masterDataFacade;
    }

    private final MasterDataFacadeLocal getMasterDataFacade() {
        return masterDataFacade;
    }

    private final LocaleFacadeLocal getLocaleFacade() {
        return localeFacade;
    }

    @Autowired
    final void setLocaleFacade(LocaleFacadeLocal localeFacade) {
        this.localeFacade = localeFacade;
    }

    /**
     * gets a generic object tree node for a specific generic object
     * @param iGenericObjectId id of generic object to get tree node for
     * @return generic object tree node for given id, if existing and allowed. null otherwise.
     * @throws CommonFinderException if the object doesn't exist (anymore).
     * @postcondition result != null
     */
    public GenericObjectTreeNode getGenericObjectTreeNode(Integer iGenericObjectId, Integer moduleId,
            Integer parentId) throws CommonFinderException {
        final GenericObjectTreeNode result = newGenericObjectTreeNode(iGenericObjectId, moduleId, null, null, null,
                parentId);
        assert result != null;
        return result;
    }

    /**
     * Note that user rights are ignored here.
     * @param iGenericObjectId
     * @param moduleId the module id
     * @param iRelationId
     * @param relationtype
     * @param direction
     * @return a new tree node for the generic object with the given id.
     * @throws CommonFinderException if the object doesn't exist (anymore).
     * @postcondition result != null
     */
    public GenericObjectTreeNode newGenericObjectTreeNode(Integer iGenericObjectId, Integer moduleId,
            Integer iRelationId, SystemRelationType relationtype, RelationDirection direction, Integer parentId)
            throws CommonFinderException {

        // @todo 1. write/use LOFB method that doesn't require the module id
        // @todo 2. Fix BUG: getWithDependants() throws a CommonPermissionException, even if bIgnoreUser == true

        final GenericObjectFacadeLocal gofacade = this.getGenericObjectFacade();

        //final int iModuleId = gofacade.getModuleContainingGenericObject(iGenericObjectId);

        final GenericObjectWithDependantsVO gowdvo = getWithDependants(gofacade, moduleId, iGenericObjectId);

        final GenericObjectTreeNode result = GenericObjectTreeNodeFactory.getInstance().newTreeNode(gowdvo,
                AttributeCache.getInstance(), serverParameterProvider, iRelationId, relationtype, direction,
                getCurrentUserName(), parentId);

        assert result != null;
        return result;
    }

    /**
     * @param lofacade
     * @param iModuleId
     * @param iGenericObjectId
     * @return the generic object with the given id, along with necessary attributes and dependants.
     */
    private static GenericObjectWithDependantsVO getWithDependants(GenericObjectFacadeLocal lofacade, int iModuleId,
            Integer iGenericObjectId) throws CommonFinderException {
        // WORKAROUND: Load only necessary attributes (and to avoid slow calculated attributes):
        // @todo move this workaround to GenericObjectFacadeBean

        final CollectableSearchExpression clctexpr = new CollectableSearchExpression(SearchConditionUtils
                .getCollectableSearchConditionForIds(Collections.singletonList(iGenericObjectId)));

        final List<GenericObjectWithDependantsVO> lstlowdcvo = lofacade.getGenericObjects(iModuleId, clctexpr,
                getAttributeIdsRequiredForGenericObjectTreeNode(iModuleId),
                getSubEntityNamesRequiredForGenericObjectTreeNode(iModuleId),
                ServerParameterProvider.getInstance().getValue(ParameterProvider.KEY_LAYOUT_CUSTOM_KEY), true);

        switch (lstlowdcvo.size()) {
        case 0:
            throw new CommonFinderException();
        case 1:
            return lstlowdcvo.get(0);
        default:
            throw new CommonFatalException(lstlowdcvo.size() + " objects found while one was expected.");
        }
    }

    private static Set<Integer> getAttributeIdsRequiredForGenericObjectTreeNode(Integer moduleId) {
        final AttributeProvider attrprovider = AttributeCache.getInstance();

        Set<Integer> result = new HashSet<Integer>();
        result.add(NuclosEOField.SYSTEMIDENTIFIER.getMetaData().getId().intValue());
        result.add(NuclosEOField.PROCESS.getMetaData().getId().intValue());
        result.add(NuclosEOField.STATE.getMetaData().getId().intValue());

        final List<MasterDataVO> lstModules = new ArrayList<MasterDataVO>();
        if (moduleId == null) {
            //choose all modules for finding required attributes for tree labels (tree node for generic search)
            lstModules.addAll(Modules.getInstance().getModules());
        } else {
            //choose requested module for finding required attributes for tree labels
            lstModules.add(Modules.getInstance().getModuleById(moduleId.intValue()));
        }

        for (final MasterDataVO mdvo : lstModules) {
            String treeView = mdvo.getField("treeview", String.class);
            if (treeView != null)
                result.addAll(CollectionUtils.transform(StringUtils.getFieldsFromTreeViewPattern(treeView),
                        new Transformer<String, Integer>() {
                            @Override
                            public Integer transform(String s) {
                                return attrprovider.getAttribute(mdvo.getField("entity", String.class), s).getId();
                            }
                        }));
        }
        return result;
    }

    private static Set<String> getSubEntityNamesRequiredForGenericObjectTreeNode(Integer iModuleId) {
        // we need no dependend subforms here. get them on demand as child subnode.
        /*final Set<String> result = new HashSet<String>();
        final MasterDataVO mdcvoModule = Modules.getInstance().getModuleById(iModuleId);
            
        final String base = (String)mdcvoModule.getField("entity");
        final Collection<EntityTreeViewVO> etvs = Modules.getInstance().getSubnodesETV(base);
        for (EntityTreeViewVO etv : etvs) {
           if (etv.isActive())
        result.add(etv.getEntity());
        }
        return result;*/
        return Collections.EMPTY_SET;
    }

    /**
     * gets the list of sub nodes for a specific generic object tree node.
     * Note that there is a specific method right on this method.
     * @param node tree node of type generic object tree node
     * @return list of sub nodes for given tree node
     * @throws CommonPermissionException
     * @postcondition result != null
     */
    public List<TreeNode> getSubNodesIgnoreUser(GenericObjectTreeNode node) {
        final List<TreeNode> result = new ArrayList<TreeNode>();
        final MasterDataVO mdcvoModule = Modules.getInstance().getModuleById(node.getModuleId());
        //add relations
        if ((Boolean) mdcvoModule.getField("showrelations")) {
            result.addAll(getRelatedSubNodes(node, RelationDirection.FORWARD));
            result.addAll(getRelatedSubNodes(node, RelationDirection.REVERSE));
            Collections.sort(result, new GenericObjectTreeNodeChildrenComparator());
        }

        // add groups
        if ((Boolean) mdcvoModule.getField("showgroups")) {
            result.addAll(getGroupSubNodes(node));
        }

        final String base = (String) mdcvoModule.getField("entity");
        final Collection<MasterDataVO> mds = Modules.getInstance().getSubnodesMD(base);
        final Collection<EntityTreeViewVO> etvs = Modules.getInstance().getSubnodesETV(base);
        subformSubnodes(result, node, mds, etvs);
        return result;
    }

    private void subformSubnodes(final List<TreeNode> result, TreeNode node, Collection<MasterDataVO> mds,
            Collection<EntityTreeViewVO> etvs) {
        final Iterator<MasterDataVO> it1 = mds.iterator();
        final Iterator<EntityTreeViewVO> it2 = etvs.iterator();
        final NavigableMap<Integer, List<? extends TreeNode>> subforms = new TreeMap<Integer, List<? extends TreeNode>>();
        // add subnodes defined in the module meta data
        while (it1.hasNext()) {
            final MasterDataVO mdvoSub = it1.next();
            final EntityTreeViewVO etv = it2.next();

            assert etv.getEntity().equals(mdvoSub.getField(EntityTreeViewVO.SUBFORM_ENTITY_FIELD));
            assert etv.getField().equals(mdvoSub.getField(EntityTreeViewVO.SUBFORM2ENTITY_REF_FIELD));

            // This seems to be all right - except for bmw-fdm. (tp)
            /*
            assert IdUtils.equals(etv.getOriginentityid(), mdvoSub.getId()) 
               : "org: " + etv.getOriginentityid() + " sub: " + mdvoSub.getIntId();
             */

            final String entity = etv.getEntity();
            final String field = etv.getField();
            final String foldername = etv.getFoldername();
            final boolean active = etv.isActive();
            final Integer sortOrder = etv.getSortOrder() == null ? Integer.valueOf(0) : etv.getSortOrder();

            if (node instanceof GenericObjectTreeNode) {
                final GenericObjectTreeNode gotn = (GenericObjectTreeNode) node;
                // check whether the user has the right to see the subform data
                Permission permission = SecurityCache.getInstance().getSubForm(getCurrentUserName(), entity)
                        .get(gotn.getStatusId());
                if (permission == null || !permission.includesReading()) {
                    continue;
                }
            }

            if (active) {
                if (!org.apache.commons.lang.StringUtils.isBlank(foldername)) {
                    if (node instanceof GenericObjectTreeNode || node instanceof MasterDataTreeNode) { // only allow Treenodes of those types.
                        SubFormTreeNode treenode = new SubFormTreeNode<Integer>(null, node, mdvoSub);
                        treenode.getSubNodes();
                        subforms.put(sortOrder, Collections.singletonList(treenode));
                    }
                } else {
                    if (Modules.getInstance().isModuleEntity(entity)) {
                        subforms.put(sortOrder, getModuleSubNodes(node, entity, field));
                    } else {
                        subforms.put(sortOrder, getMasterDataSubNodes(node, entity, field));
                    }
                }
            }
        }
        for (List<? extends TreeNode> l : subforms.values()) {
            result.addAll(l);
        }
    }

    /**
     * get subnodes of type sEntity where the valueId of sField corresponds to node.getId()
     * @param node
     * @param sEntity
     * @param sField
     * @return
     * @throws NoSuchElementException
     * @throws CommonFatalException
     */
    private List<TreeNode> getModuleSubNodes(TreeNode node, final String sEntity, final String sField)
            throws NoSuchElementException {
        final Integer moduleId = Modules.getInstance().getModuleIdByEntityName(sEntity);
        final Integer nodeId = (Integer) node.getId();
        if (nodeId == null)
            return Collections.emptyList();
        CollectableComparison cond = SearchConditionUtils.newEOidComparison(sEntity, sField,
                ComparisonOperator.EQUAL, IdUtils.toLongId(nodeId), MetaDataServerProvider.getInstance());

        List<Integer> lstIds = getGenericObjectFacade().getGenericObjectIds(moduleId, cond);

        List<TreeNode> result = CollectionUtils.transform(lstIds, new Transformer<Integer, TreeNode>() {

            @Override
            public TreeNode transform(Integer genericObjectId) {
                try {
                    GenericObjectTreeNode node = newGenericObjectTreeNode(genericObjectId, moduleId, null, null,
                            null, nodeId);
                    return node;
                } catch (CommonFinderException e) {
                    throw new NuclosFatalException(e);
                }
            }
        });
        return result;
    }

    /**
     * Note that user rights are always ignored here.
     * @param node
     * @param direction
     * @return the given node's subnodes related in the given direction (excluding GenericObjectTreeNode.RELATIONTYPE_INVOICE_OF).
     * @throws CommonPermissionException
     */
    private List<TreeNode> getRelatedSubNodes(GenericObjectTreeNode node, final RelationDirection direction) {
        final List<TreeNode> result = new ArrayList<TreeNode>();

        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<DbTuple> query = builder.createTupleQuery();
        DbFrom r = query.from("T_UD_GO_RELATION").alias("r");
        DbFrom l = query.from("T_UD_GENERICOBJECT").alias("l");
        DbColumnExpression<Integer> genericObject1 = r
                .baseColumn(direction.isForward() ? "INTID_T_UD_GO_1" : "INTID_T_UD_GO_2", Integer.class);
        DbColumnExpression<Integer> genericObject2 = r
                .baseColumn(direction.isForward() ? "INTID_T_UD_GO_2" : "INTID_T_UD_GO_1", Integer.class);
        query.multiselect(r.baseColumn("INTID", Integer.class),
                //genericObject2,
                l.baseColumn("INTID", Integer.class), r.baseColumn("STRRELATIONTYPE", String.class),
                l.baseColumn("INTID_T_MD_MODULE", Integer.class));
        query.where(builder.and(builder.equal(genericObject2, l.baseColumn("INTID", Integer.class)),
                node.getParentId() != null ? builder.equal(genericObject2, node.getParentId()).not()
                        : builder.alwaysTrue(),
                builder.equal(genericObject1, node.getId())//,
        //   builder.equal(l.column("BLNDELETED", Boolean.class), false)));
        ));
        // order by type ???

        for (DbTuple tuple : dataBaseHelper.getDbAccess().executeQuery(query.distinct(true))) {
            final Integer iRelationId = tuple.get(0, Integer.class);
            final int iGenericObjectId = tuple.get(1, Integer.class);

            final String relationType = tuple.get(2, String.class);
            int moduleId = tuple.get(3, Integer.class);

            SystemRelationType systemRelationType = SystemRelationType.findSystemRelationType(relationType);
            if (systemRelationType != null) {
                // predecessor or part-of relation:
                try {
                    result.add(newGenericObjectTreeNode(iGenericObjectId, moduleId, iRelationId, systemRelationType,
                            direction, node.getId()));
                } catch (CommonFinderException ex) {
                    // the object doesn't exist anymore - ignore.
                }
            } else {
                try {
                    final GenericObjectTreeNode nodeRelatedObject = newGenericObjectTreeNode(iGenericObjectId,
                            moduleId, null, null, null, node.getId());
                    String label = getRelationTypeLabel(relationType);
                    result.add(
                            new RelationTreeNode(iRelationId, label, relationType, direction, nodeRelatedObject));
                } catch (CommonFinderException ex) {
                    // the object doesn't exist anymore - ignore.
                }
            }
        }

        return result;
    }

    private String getRelationTypeLabel(String relationType) {
        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<String> query = builder.createQuery(String.class);
        DbFrom t = query.from("T_MD_RELATIONTYPE").alias(SystemFields.BASE_ALIAS);
        query.select(t.baseColumn("STRLOCALERESOURCEID", String.class));
        query.where(builder.equal(t.baseColumn("STRRELATIONTYPE", String.class), relationType));
        String resourceId = CollectionUtils.getFirst(dataBaseHelper.getDbAccess().executeQuery(query));
        if (resourceId != null) {
            SpringLocaleDelegate.getInstance().getMessage(resourceId, relationType);
        }
        return null;
    }

    private List<TreeNode> getGroupSubNodes(GenericObjectTreeNode node) {
        final List<TreeNode> result = new ArrayList<TreeNode>();

        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<DbTuple> query = builder.createTupleQuery();
        DbFrom l = query.from("T_UD_GO_GROUP").alias("l");
        DbFrom g = l.join("T_UD_GROUP", JoinType.INNER).alias("g").on("INTID_T_UD_GROUP", "INTID", Integer.class);
        DbFrom t = g.join("T_MD_GROUPTYPE", JoinType.INNER).alias(SystemFields.BASE_ALIAS)
                .on("INTID_T_MD_GROUPTYPE", "INTID", Integer.class);
        query.multiselect(l.baseColumn("INTID_T_UD_GROUP", Integer.class), g.baseColumn("STRGROUP", String.class),
                g.baseColumn("STRDESCRIPTION", String.class), t.baseColumn("STRNAME", String.class));
        query.where(builder.equal(l.baseColumn("INTID_T_UD_GROUP", Integer.class), node.getId()));
        query.orderBy(builder.asc(g.baseColumn("STRGROUP", String.class)),
                builder.desc(g.baseColumn("DATVALIDFROM", Date.class)));

        for (DbTuple tuple : dataBaseHelper.getDbAccess().executeQuery(query)) {
            final GroupTreeNode nodeGroupDefined = new GroupTreeNode(tuple.get(0, Integer.class),
                    tuple.get(1, String.class), tuple.get(2, String.class), tuple.get(3, String.class));
            nodeGroupDefined.getSubNodes();
            result.add(nodeGroupDefined);
        }

        return result;
    }

    // group:

    /**
     * method to get a group tree node for a specific group
     * @param iId id of group to get tree node for
     * @return group tree node for given id
     * @postcondition result != null
     */
    public GroupTreeNode getGroupTreeNode(final Integer iId, final boolean bLoadSubNodes)
            throws CommonFinderException {
        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<DbTuple> query = builder.createTupleQuery();
        DbFrom t = query.from("T_UD_GROUP").alias(SystemFields.BASE_ALIAS);
        DbFrom fk1 = t.join("T_MD_GROUPTYPE", JoinType.INNER).alias("fk1").on("INTID_T_MD_GROUPTYPE", "INTID",
                Integer.class);
        query.multiselect(t.baseColumn("STRGROUP", String.class), fk1.baseColumn("STRNAME", String.class),
                t.baseColumn("STRDESCRIPTION", String.class));
        query.where(builder.equal(t.baseColumn("INTID", Integer.class), iId));

        DbTuple tuple;
        try {
            tuple = dataBaseHelper.getDbAccess().executeQuerySingleResult(query);
        } catch (DbInvalidResultSizeException e) {
            throw new NuclosFatalException("treenode.error.missing.group");//"Die Gruppe existiert nicht mehr.");
        }

        final GroupTreeNode result = new GroupTreeNode(iId, tuple.get(0, String.class), tuple.get(1, String.class),
                tuple.get(2, String.class));

        if (bLoadSubNodes) {
            result.getSubNodes();
        }
        return result;
    }

    public NucletTreeNode getNucletTreeNode(Integer iId) throws CommonFinderException {
        try {
            EntityObjectVO eovo = NucletDalProvider.getInstance().getEntityObjectProcessor(NuclosEntity.NUCLET)
                    .getByPrimaryKey(iId.longValue());
            return new NucletTreeNode(eovo, false);
        } catch (Exception ex) {
            throw new CommonFinderException();
        }
    }

    /**
     * method to get a masterdata tree node for a specific masterdata record
     * @param iId id of masterdata record to get tree node for
     * @return masterdata tree node for given id
     * @throws CommonPermissionException
     * @postcondition result != null
     */
    public MasterDataTreeNode<Integer> getMasterDataTreeNode(Integer iId, String sEntity, boolean bLoadSubNodes)
            throws CommonFinderException, CommonPermissionException {
        final MasterDataVO mdvo = this.getMasterDataFacade().get(sEntity, iId);
        final MasterDataTreeNode<Integer> result = new DefaultMasterDataTreeNode(sEntity, mdvo);

        if (bLoadSubNodes) {
            result.getSubNodes();
        }
        assert result != null;
        return result;
    }

    public SubFormEntryTreeNode getSubFormEntryTreeNode(Integer iId, String sEntity, boolean bLoadSubNodes)
            throws CommonFinderException, CommonPermissionException {
        final MasterDataVO mdvo = this.getMasterDataFacade().get(sEntity, iId);
        final SubFormEntryTreeNode result = new SubFormEntryTreeNode(sEntity, mdvo);

        if (bLoadSubNodes) {
            result.getSubNodes();
        }
        assert result != null;
        return result;
    }

    /**
     * @param node
     * @return the subnodes for the given node.
     * @postcondition result != null
     */
    public List<GenericObjectTreeNode> getSubNodes(GroupTreeNode node) {
        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<DbTuple> query = builder.createTupleQuery();
        DbFrom grp = query.from("T_UD_GO_GROUP").alias("grp");
        DbFrom gob = grp.join("T_UD_GENERICOBJECT", JoinType.INNER).alias("gob").on("INTID_T_UD_GENERICOBJECT",
                "INTID", Integer.class);
        query.multiselect(grp.baseColumn("INTID_T_UD_GENERICOBJECT", Integer.class),
                gob.baseColumn("INTID_T_MD_MODULE", Integer.class));
        query.where(builder.equal(grp.baseColumn("INTID_T_UD_GROUP", Integer.class), node.getId()));
        query.orderBy(builder.asc(grp.baseColumn("DATCREATED", Date.class)));

        final List<GenericObjectTreeNode> result = new ArrayList<GenericObjectTreeNode>();
        for (DbTuple tuple : dataBaseHelper.getDbAccess().executeQuery(query)) {
            try {
                result.add(
                        getGenericObjectTreeNode(tuple.get(0, Integer.class), tuple.get(1, Integer.class), null));
            } catch (CommonFinderException ex) {
                // the object doesn't exist anymore - ignore.
            }
        }
        Collections.sort(result);
        return result;
    }

    public List<TreeNode> getSubNodes(NucletTreeNode node) {
        List<TreeNode> result = new ArrayList<TreeNode>();
        if (node.isShowDependeces()) {

            CollectableSearchCondition cond = org.nuclos.common.SearchConditionUtils.newEOidComparison(
                    NuclosEntity.NUCLETDEPENDENCE.getEntityName(), "nuclet", ComparisonOperator.EQUAL,
                    IdUtils.toLongId(node.getId()), MetaDataServerProvider.getInstance());

            List<NucletTreeNode> nucletNodes = new ArrayList<NucletTreeNode>();
            for (EntityObjectVO eoDependence : NucletDalProvider.getInstance()
                    .getEntityObjectProcessor(NuclosEntity.NUCLETDEPENDENCE)
                    .getBySearchExpression(new CollectableSearchExpression(cond))) {
                EntityObjectVO eoNuclet = NucletDalProvider.getInstance()
                        .getEntityObjectProcessor(NuclosEntity.NUCLET)
                        .getByPrimaryKey(eoDependence.getFieldId("nucletDependence"));
                nucletNodes.add(new NucletTreeNode(eoNuclet, true));
            }
            result.addAll(CollectionUtils.sorted(nucletNodes, new Comparator<NucletTreeNode>() {
                @Override
                public int compare(NucletTreeNode o1, NucletTreeNode o2) {
                    return LangUtils.compare(o1.getLabel(), o2.getLabel());
                }
            }));
        } else {
            result.addAll(getNucletContentTypes(IdUtils.toLongId(node.getId())));
        }
        return result;
    }

    private List<NucletContentTreeNode> getNucletContentTypes(Long nucletId) {
        List<NucletContentTreeNode> result = new ArrayList<NucletContentTreeNode>();
        for (NuclosEntity ne : AbstractNucletContentEntryTreeNode.getNucletContentEntities()) {
            switch (ne) {
            case REPORT:
                result.add(new ReportNucletContentTreeNode(nucletId));
                break;
            default:
                result.add(new NucletContentTreeNode(nucletId, ne));
            }
        }
        return result;
    }

    /**
     * @param node
     * @return the subnodes for the given node.
     * @postcondition result != null
     */
    public List<TreeNode> getSubnodes(DefaultMasterDataTreeNode node) {
        final List<TreeNode> result = new ArrayList<TreeNode>();
        final MasterDataMetaVO mdMeta = MasterDataMetaCache.getInstance().getMetaData(node.getEntityName());
        final Collection<MasterDataVO> colSubNodes = MasterDataMetaCache.getInstance()
                .getSubnodesMD(node.getEntityName(), mdMeta.getId());
        final Collection<EntityTreeViewVO> subnodes = MasterDataMetaCache.getInstance()
                .getSubnodesETV(node.getEntityName(), mdMeta.getId());

        subformSubnodes(result, node, colSubNodes, subnodes);
        return result;
    }

    /**
     * @param node
     * @return the subnodes for the given node.
     * @postcondition result != null
     */
    public List<AbstractNucletContentEntryTreeNode> getSubNodes(NucletContentTreeNode node) {
        final List<AbstractNucletContentEntryTreeNode> result = new ArrayList<AbstractNucletContentEntryTreeNode>();

        EntityMetaDataVO eMeta = MetaDataServerProvider.getInstance().getEntity(node.getEntity());
        EntityFieldMetaDataVO efMetaNuclet = MetaDataServerProvider.getInstance().getEntityField(eMeta.getEntity(),
                AbstractNucletContentEntryTreeNode.FOREIGN_FIELD_TO_NUCLET);

        CollectableSearchCondition cond = org.nuclos.common.SearchConditionUtils.newEOidComparison(
                eMeta.getEntity(), efMetaNuclet.getField(), ComparisonOperator.EQUAL, node.getNucletId(),
                MetaDataServerProvider.getInstance());

        for (EntityObjectVO eo : NucletDalProvider.getInstance().getEntityObjectProcessor(node.getEntity())
                .getBySearchExpression(new CollectableSearchExpression(cond))) {
            result.add(getNucletContentEntryNode(eo));
        }

        return sortAbstractNucletContentEntryTreeNodes(result);
    }

    public List<AbstractNucletContentEntryTreeNode> getNucletContent(NucletTreeNode node) {
        final List<AbstractNucletContentEntryTreeNode> result = new ArrayList<AbstractNucletContentEntryTreeNode>();

        for (NucletContentTreeNode contentTypeNode : getNucletContentTypes(IdUtils.toLongId(node.getId()))) {
            result.addAll(getSubNodes(contentTypeNode));
        }
        return result;
    }

    public List<NucletTreeNode> getSubNodes(NuclosInstanceTreeNode node) {
        final Collection<NucletTreeNode> result = new ArrayList<NucletTreeNode>();

        EntityMetaDataVO eMetaDependence = MetaDataServerProvider.getInstance()
                .getEntity(NuclosEntity.NUCLETDEPENDENCE);
        EntityFieldMetaDataVO efMetaDependence = MetaDataServerProvider.getInstance()
                .getEntityField(eMetaDependence.getEntity(), "nucletDependence");

        for (EntityObjectVO eoNuclet : NucletDalProvider.getInstance().getEntityObjectProcessor(NuclosEntity.NUCLET)
                .getAll()) {

            CollectableSearchCondition cond = org.nuclos.common.SearchConditionUtils.newEOidComparison(
                    eMetaDependence.getEntity(), efMetaDependence.getField(), ComparisonOperator.EQUAL,
                    eoNuclet.getId(), MetaDataServerProvider.getInstance());

            if (NucletDalProvider.getInstance().getEntityObjectProcessor(NuclosEntity.NUCLETDEPENDENCE)
                    .count(new CollectableSearchExpression(cond)) == 0) {
                // is root Nuclet
                result.add(new NucletTreeNode(eoNuclet, true));
            }
        }

        return CollectionUtils.sorted(result, new Comparator<NucletTreeNode>() {
            @Override
            public int compare(NucletTreeNode o1, NucletTreeNode o2) {
                return LangUtils.compare(o1.getLabel(), o2.getLabel());
            }
        });
    }

    public List<AbstractNucletContentEntryTreeNode> getAvailableNucletContents() {
        List<AbstractNucletContentEntryTreeNode> result = new ArrayList<AbstractNucletContentEntryTreeNode>();
        for (NuclosEntity ne : AbstractNucletContentEntryTreeNode.getNucletContentEntities()) {
            EntityMetaDataVO eMeta = MetaDataServerProvider.getInstance().getEntity(ne);
            EntityFieldMetaDataVO efMetaNuclet = MetaDataServerProvider.getInstance()
                    .getEntityField(eMeta.getEntity(), AbstractNucletContentEntryTreeNode.FOREIGN_FIELD_TO_NUCLET);

            CollectableSearchCondition cond = org.nuclos.common.SearchConditionUtils.newEOIsNullComparison(
                    eMeta.getEntity(), efMetaNuclet.getField(), ComparisonOperator.IS_NULL,
                    MetaDataServerProvider.getInstance());

            switch (ne) {
            case WORKSPACE:
                cond = SearchConditionUtils.and(cond, SearchConditionUtils.newEOIsNullComparison(ne.getEntityName(),
                        "user", ComparisonOperator.IS_NULL, MetaDataServerProvider.getInstance()));
                break;
            }

            List<AbstractNucletContentEntryTreeNode> nodes = new ArrayList<AbstractNucletContentEntryTreeNode>();
            for (EntityObjectVO eo : NucletDalProvider.getInstance().getEntityObjectProcessor(ne)
                    .getBySearchExpression(new CollectableSearchExpression(cond))) {
                nodes.add(getNucletContentEntryNode(eo));
            }
            result.addAll(sortAbstractNucletContentEntryTreeNodes(nodes));
        }
        return result;
    }

    public void addNucletContents(Long nucletId, Set<AbstractNucletContentEntryTreeNode> contents)
            throws NuclosBusinessException {
        CacheInvalidator ci = new CacheInvalidator();

        for (AbstractNucletContentEntryTreeNode node : contents) {
            final JdbcEntityObjectProcessor processor = NucletDalProvider.getInstance()
                    .getEntityObjectProcessor(node.getEntity());
            final EntityObjectVO eo = processor.getByPrimaryKey(node.getId()); //reload the content, no version check here!

            if (eo.getFieldIds().get(AbstractNucletContentEntryTreeNode.FOREIGN_FIELD_TO_NUCLET) != null) {
                if (LangUtils.equals(nucletId,
                        eo.getFieldIds().get(AbstractNucletContentEntryTreeNode.FOREIGN_FIELD_TO_NUCLET))) {
                    continue;
                } else {
                    throw new NuclosBusinessException("treenode.facade.businessexception.1");
                }
            }

            eo.getFieldIds().put(AbstractNucletContentEntryTreeNode.FOREIGN_FIELD_TO_NUCLET, nucletId);
            eo.flagUpdate();
            DalUtils.updateVersionInformation(eo, getCurrentUserName());
            processor.insertOrUpdate(eo);

            ci.handleNode(node);
        }

        ci.run();
    }

    public boolean removeNucletContents(Set<AbstractNucletContentEntryTreeNode> contents) {
        boolean result = false;
        CacheInvalidator ci = new CacheInvalidator();

        for (AbstractNucletContentEntryTreeNode node : contents) {
            final JdbcEntityObjectProcessor processor = NucletDalProvider.getInstance()
                    .getEntityObjectProcessor(node.getEntity());
            final EntityObjectVO eo = processor.getByPrimaryKey(node.getId()); //reload the content, no version check here!

            if (eo.getFieldIds().containsKey(AbstractNucletContentEntryTreeNode.FOREIGN_FIELD_TO_NUCLET)) {
                result = true;
                eo.getFieldIds().put(AbstractNucletContentEntryTreeNode.FOREIGN_FIELD_TO_NUCLET, null);
                eo.flagUpdate();
                DalUtils.updateVersionInformation(eo, getCurrentUserName());
                processor.insertOrUpdate(eo);
            }

            if (result)
                ci.handleNode(node);
        }

        ci.run();
        return result;
    }

    private class CacheInvalidator {
        boolean invalidateRuleCache = false;
        boolean invalidateDatasourceCache = false;

        public void handleNode(AbstractNucletContentEntryTreeNode node) {
            if (node.getEntity() == NuclosEntity.RULE || node.getEntity() == NuclosEntity.TIMELIMITRULE
                    || node.getEntity() == NuclosEntity.CODE) {
                invalidateRuleCache = true;
            }
            if (node.getEntity() == NuclosEntity.DATASOURCE || node.getEntity() == NuclosEntity.DYNAMICENTITY
                    || node.getEntity() == NuclosEntity.VALUELISTPROVIDER
                    || node.getEntity() == NuclosEntity.RECORDGRANT || node.getEntity() == NuclosEntity.CHART) {
                invalidateDatasourceCache = true;
            }
        }

        public void run() {
            if (invalidateRuleCache)
                RuleCache.getInstance().invalidate();
            if (invalidateDatasourceCache)
                DatasourceCache.getInstance().invalidate();
        }
    }

    private List<AbstractNucletContentEntryTreeNode> sortAbstractNucletContentEntryTreeNodes(
            List<AbstractNucletContentEntryTreeNode> nodes) {
        return CollectionUtils.sorted(nodes, new AbstractNucletContentEntryTreeNode.Comparator());
    }

    public AbstractNucletContentEntryTreeNode getNucletContentEntryNode(NuclosEntity entity, Long eoId) {
        return getNucletContentEntryNode(
                NucletDalProvider.getInstance().getEntityObjectProcessor(entity).getByPrimaryKey(eoId));
    }

    private AbstractNucletContentEntryTreeNode getNucletContentEntryNode(EntityObjectVO eo) {
        if (eo == null) {
            throw new IllegalArgumentException("eo must not be null");
        }
        NuclosEntity entity = NuclosEntity.getByName(eo.getEntity());
        if (entity == null) {
            throw new IllegalArgumentException("entity object must be nuclos entity");
        }
        switch (entity) {
        case ENTITY:
            return new NucletContentEntityTreeNode(eo);
        case CUSTOMCOMPONENT:
            return new NucletContentCustomComponentTreeNode(eo);
        case RULE:
            return new NucletContentRuleTreeNode(eo);
        case PROCESS:
            return new NucletContentProcessTreeNode(eo);
        default:
            return new DefaultNucletContentEntryTreeNode(eo);
        }
    }

    public DynamicTreeNode<Integer> getDynamicTreeNode(TreeNode node, MasterDataVO mdVO) {
        return new DynamicTreeNode<Integer>(null, node, mdVO);
    }

    public SubFormTreeNode getSubFormTreeNode(TreeNode node, MasterDataVO mdVO) {
        return new SubFormTreeNode(null, node, mdVO);
    }

    public List<TreeNode> getSubNodesForDynamicTreeNode(TreeNode node, MasterDataVO mdVO) {
        final String sEntity = (String) mdVO.getField("entity");
        final String sField = (String) mdVO.getField("field");

        List<TreeNode> result = new ArrayList<TreeNode>();
        if (Modules.getInstance().isModuleEntity(sEntity)) {
            result.addAll(getModuleSubNodes(node, sEntity, sField));
        } else {
            result.addAll(getMasterDataSubNodes(node, sEntity, sField));
        }

        return result;
    }

    public List<SubFormEntryTreeNode> getSubNodesForSubFormTreeNode(TreeNode node, MasterDataVO mdVO) {
        final String sEntity = (String) mdVO.getField("entity");
        final String sField = (String) mdVO.getField("field");

        final Collection<MasterDataVO> colmdvo = this.getMasterDataFacade().getDependantMasterData(sEntity, sField,
                node.getId());
        final Collection<SubFormEntryTreeNode> colResult = CollectionUtils.transform(colmdvo,
                new Transformer<MasterDataVO, SubFormEntryTreeNode>() {
                    @Override
                    public SubFormEntryTreeNode transform(MasterDataVO i) {
                        return new SubFormEntryTreeNode(sEntity, i);
                    }
                });

        final List<SubFormEntryTreeNode> result = new ArrayList<SubFormEntryTreeNode>(colResult);
        Collections.sort(result);
        return result;
    }

    /**
     * get the masterdata subnodes for the given node
     * @param node
     * @param sEntity
     * @param sField
     * @return
     */
    private List<DefaultMasterDataTreeNode> getMasterDataSubNodes(TreeNode node, final String sEntity,
            String sField) {
        final Collection<MasterDataVO> colmdvo = this.getMasterDataFacade().getDependantMasterData(sEntity, sField,
                node.getId());
        final Collection<DefaultMasterDataTreeNode> colResult = CollectionUtils.transform(colmdvo,
                new Transformer<MasterDataVO, DefaultMasterDataTreeNode>() {
                    @Override
                    public DefaultMasterDataTreeNode transform(MasterDataVO i) {
                        return new DefaultMasterDataTreeNode(sEntity, i);
                    }
                });

        final List<DefaultMasterDataTreeNode> result = new ArrayList<DefaultMasterDataTreeNode>(colResult);
        Collections.sort(result);
        return result;

    }

    public java.util.List<org.nuclos.server.navigation.treenode.GroupTreeNode> getSubNodes(
            GroupSearchResultTreeNode node) {
        final List<GroupTreeNode> result = new ArrayList<GroupTreeNode>();

        for (MasterDataVO mdvo : this.getMasterDataFacade().getMasterData(NuclosEntity.GROUP.getEntityName(),
                appendRecordGrants(node.getSearchCondition(), NuclosEntity.GROUP.getEntityName()), false)) {
            result.add(new GroupTreeNode(mdvo.getIntId(), mdvo.getField("name", String.class),
                    mdvo.getField("grouptype", String.class), mdvo.getField("description", String.class)));
        }

        Collections.sort(result);
        assert result != null;
        return result;
    }

    /**
     * get the subnodes for a masterdata search result
     * @param node
     * @return the subnodes for the given node.
     * @postcondition result != null
     */
    public java.util.List<org.nuclos.server.navigation.treenode.DefaultMasterDataTreeNode> getSubNodes(
            MasterDataSearchResultTreeNode node) {
        final List<DefaultMasterDataTreeNode> result = new ArrayList<DefaultMasterDataTreeNode>();

        for (MasterDataVO mdvo : this.getMasterDataFacade().getMasterData(node.getEntity(),
                appendRecordGrants(node.getSearchCondition(), node.getEntity()), false)) {
            result.add(new DefaultMasterDataTreeNode(node.getEntity(), mdvo));
        }

        Collections.sort(result);
        assert result != null;
        return result;
    }

    /**
     * method to get the list of sub nodes for a specific generic object search result tree node
     * @param node tree node of type search result tree node
     * @return list of sub nodes for given tree node
     * @postcondition result != null
     */
    public List<TreeNode> getSubNodes(EntitySearchResultTreeNode node) {
        final int iMaxRowCount = serverParameterProvider.getIntValue(
                ParameterProvider.KEY_MAX_ROWCOUNT_FOR_SEARCHRESULT_IN_TREE, DEFAULT_ROWCOUNT_FOR_SEARCHRESULT);

        if (Modules.getInstance().isModuleEntity(node.getEntity())) {
            final AttributeProvider attrprovider = AttributeCache.getInstance();

            final List<TreeNode> result = new ArrayList<TreeNode>(
                    Math.min(iMaxRowCount, DEFAULT_ROWCOUNT_FOR_SEARCHRESULT));
            Integer iModuleId = Modules.getInstance().getModuleIdByEntityName(node.getEntity());
            final TruncatableCollection<GenericObjectWithDependantsVO> collgowdvo = this.getGenericObjectFacade()
                    .getRestrictedNumberOfGenericObjects(iModuleId,
                            appendRecordGrants(node.getSearchExpression(), node.getEntity()),
                            getAttributeIdsRequiredForGenericObjectTreeNode(iModuleId),
                            getSubEntityNamesRequiredForGenericObjectTreeNode(iModuleId),
                            ServerParameterProvider.getInstance().getValue(ParameterProvider.KEY_LAYOUT_CUSTOM_KEY),
                            iMaxRowCount);

            for (GenericObjectWithDependantsVO gowdvo : collgowdvo) {
                result.add(GenericObjectTreeNodeFactory.getInstance().newTreeNode(gowdvo, attrprovider,
                        serverParameterProvider, null, null, null, getCurrentUserName(), null));
            }

            String sLabel = MessageFormat.format(
                    getLocaleFacade().getResourceById(getLocaleFacade().getUserLocale(), "treenode.subnode.label"),
                    node.getLabel(), collgowdvo.size(), collgowdvo.totalSize());
            if (collgowdvo.isTruncated()) {
                node.setLabel(sLabel);
                //node.getLabel() + " (begrenzt auf " + collgowdvo.size() + " von " + collgowdvo.totalSize() + " Ergebnissen)");
            }
            /** @todo OPTIMIZE: sort in the database! */
            //Collections.sort(result);

            assert result != null;
            return result;
        } else {
            final List<TreeNode> result = new ArrayList<TreeNode>(
                    Math.min(iMaxRowCount, DEFAULT_ROWCOUNT_FOR_SEARCHRESULT));

            final TruncatableCollection<MasterDataVO> collmdvo = this.getMasterDataFacade().getMasterData(
                    node.getEntity(),
                    appendRecordGrants(node.getSearchExpression(), node.getEntity()).getSearchCondition(), false);
            for (MasterDataVO mdvo : collmdvo) {
                result.add(new DefaultMasterDataTreeNode(node.getEntity(), mdvo));
            }
            String sLabel = MessageFormat.format(
                    getLocaleFacade().getResourceById(getLocaleFacade().getUserLocale(), "treenode.subnode.label"),
                    node.getLabel(), collmdvo.size(), collmdvo.totalSize());
            if (collmdvo.isTruncated()) {
                node.setLabel(sLabel);
                //node.getLabel() + " begrenzt auf " + collmdvo.size() + " von " + collmdvo.totalSize() + " Ergebnissen)");
            }

            //Collections.sort(result);
            assert result != null;
            return result;
        }
    }

    private static class GenericObjectTreeNodeChildrenComparator implements Comparator<TreeNode> {
        @Override
        public int compare(TreeNode tn1, TreeNode tn2) {
            int result = getOrder(tn1) - getOrder(tn2);
            if (result == 0) {
                if (tn1 instanceof GenericObjectTreeNode) {
                    result = ((Comparable<GenericObjectTreeNode>) tn1).compareTo((GenericObjectTreeNode) tn2);
                } else if (tn1 instanceof RelationTreeNode) {
                    result = ((Comparable<TreeNode>) tn1).compareTo(tn2);
                } else {
                    throw new CommonFatalException("Cannot compare the given TreeNodes.");
                }
            }
            return result;
        }

        private int getOrder(TreeNode treenode) {
            return LangUtils.isInstanceOf(treenode, GenericObjectTreeNode.class) ? 1 : 2;
        }

    } // inner class GenericObjectTreeNodeChildrenComparator
}