<commit_before>/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This 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 software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package com.celements.navigation.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.annotation.Requirement;
import org.xwiki.context.Execution;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.EntityReferenceSerializer;

import com.celements.inheritor.InheritorFactory;
import com.celements.navigation.Navigation;
import com.celements.navigation.TreeNode;
import com.celements.navigation.filter.INavFilter;
import com.celements.navigation.filter.InternalRightsFilter;
import com.celements.web.service.IWebUtilsService;
import com.celements.web.service.WebUtilsService;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseCollection;
import com.xpn.xwiki.objects.BaseObject;

/**
 * TreeNodeService
 * 
 * access a tree node structure through the method of this service.
 * 
 * @author <NAME>
 *
 */
@Component
public class TreeNodeService implements ITreeNodeService {

  private static Log LOGGER = LogFactory.getFactory().getInstance(TreeNodeService.class);

  @Requirement
  Execution execution;

  @Requirement
  ITreeNodeCache treeNodeCache;
  
  @Requirement("default")
  EntityReferenceSerializer<String> serializer;

  @Requirement
  IWebUtilsService webUtilsService;

  private InheritorFactory injectedInheritorFactory;

  @Requirement(role = ITreeNodeProvider.class)
  Map<String, ITreeNodeProvider> nodeProviders;

  private XWikiContext getContext() {
    return (XWikiContext)execution.getContext().getProperty("xwikicontext");
  }
    
  public int getActiveMenuItemPos(int menuLevel, String menuPart) {
    List<DocumentReference> parents = webUtilsService.getDocumentParentsList(
        getContext().getDoc().getDocumentReference(), true);
    if (parents.size() >= menuLevel) {
      return getMenuItemPos(parents.get(parents.size() - menuLevel), menuPart);
    }
    return -1;
  }

  public int getMenuItemPos(DocumentReference docRef, String menuPart) {
    try {
      DocumentReference parent = getParentRef(docRef);
      int pos = -1;
      for (TreeNode menuItem : getSubNodesForParent(parent, menuPart)) {
        pos = pos + 1;
        if (docRef.equals(menuItem.getDocumentReference())) {
          return pos;
        }
      }
    } catch (XWikiException e) {
      LOGGER.error(e);
    }
    return -1;
  }
  
  /**
   * 
   * @deprecated since 2.17.0 use getSubNodesForParent(EntityReference, INavFilter)  or 
   * getSubNodesForParent(EntityReference, String) instead
   */
  @Deprecated
  public <T> List<TreeNode> getSubNodesForParent(String parent, String menuSpace,
      INavFilter<T> filter) {
    if("".equals(menuSpace)) {
      menuSpace = getContext().getDoc().getDocumentReference().getLastSpaceReference(
          ).getName();
    }
    String parentKey = getParentKey(parent, menuSpace);
    return getSubNodesForParent(getRef(parentKey), filter);
  }

  String getParentKey(String parent, String menuSpace) {
    String parentKey = "";
    if (parent != null) {
      parentKey = parent;
    }
    if(parentKey.indexOf('.') < 0) {
      parentKey = menuSpace + "." + parentKey;
    }
    if(parentKey.indexOf(':') < 0) {
      parentKey = getContext().getDatabase() + ":" + parentKey;
    }
    return parentKey;
  }
  
  String getParentKey(EntityReference reference, boolean withDataBase) {
    String parentKey = "";
    if (reference != null) {
      parentKey = serializer.serialize(reference);
      if (!parentKey.contains(":") && !parentKey.endsWith(":")) {
        parentKey += ":";
      } else if (!parentKey.contains(".") && !parentKey.endsWith(".")) {
        parentKey += ".";
      }
      if (!withDataBase) {
        parentKey = parentKey.substring(parentKey.indexOf(":") + 1);
      }
    }
    LOGGER.debug("getParentKey: returning [" + parentKey + "] for entityref ["
        + reference + "].");
    return parentKey;
  }
  
  public <T> List<TreeNode> getSubNodesForParent(EntityReference entRef,
      INavFilter<T> filter) {
    ArrayList<TreeNode> menuArray = new ArrayList<TreeNode>();
    for(TreeNode node : fetchNodesForParentKey(entRef)) {
      if((node!=null) && filter.includeTreeNode(node, getContext())) {
        // show only Menuitems of pages accessible to the current user
        menuArray.add(node);
      }
    }
    return menuArray;
  }
  
  /**
   * 
   * @deprecated since 2.17.0 use getSubNodesForParent(EntityReference, INavFilter)  or 
   * getSubNodesForParent(EntityReference, String) instead
   */
  @Deprecated
  public List<TreeNode> getSubNodesForParent(String parent, String menuSpace,
      String menuPart) {
    InternalRightsFilter filter = new InternalRightsFilter();
    filter.setMenuPart(menuPart);
    return getSubNodesForParent(parent, menuSpace, filter);
  }
  
  public List<TreeNode> getSubNodesForParent(EntityReference entRef,
      String menuPart) {
    InternalRightsFilter filter = new InternalRightsFilter();
    filter.setMenuPart(menuPart);
    return getSubNodesForParent(entRef, filter);
  }

  /**
   * fetchNodesForParentKey
   * @param parentKey
   * @param context
   * @return Collection keeps ordering of TreeNodes according to posId
   */
  List<TreeNode> fetchNodesForParentKey(EntityReference reference) {
    String parentKey = getParentKey(reference, false);<commit_msg>add logging to deprecated getSubNodesForParent in TreeNodeService.<commit_after>/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This 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 software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package com.celements.navigation.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.annotation.Requirement;
import org.xwiki.context.Execution;
import org.xwiki.model.EntityType;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.EntityReferenceSerializer;

import com.celements.inheritor.InheritorFactory;
import com.celements.navigation.Navigation;
import com.celements.navigation.TreeNode;
import com.celements.navigation.filter.INavFilter;
import com.celements.navigation.filter.InternalRightsFilter;
import com.celements.web.service.IWebUtilsService;
import com.celements.web.service.WebUtilsService;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseCollection;
import com.xpn.xwiki.objects.BaseObject;

/**
 * TreeNodeService
 * 
 * access a tree node structure through the method of this service.
 * 
 * @author <NAME>
 *
 */
@Component
public class TreeNodeService implements ITreeNodeService {

  private static Log LOGGER = LogFactory.getFactory().getInstance(TreeNodeService.class);

  @Requirement
  Execution execution;

  @Requirement
  ITreeNodeCache treeNodeCache;
  
  @Requirement("default")
  EntityReferenceSerializer<String> serializer;

  @Requirement
  IWebUtilsService webUtilsService;

  private InheritorFactory injectedInheritorFactory;

  @Requirement(role = ITreeNodeProvider.class)
  Map<String, ITreeNodeProvider> nodeProviders;

  private XWikiContext getContext() {
    return (XWikiContext)execution.getContext().getProperty("xwikicontext");
  }
    
  public int getActiveMenuItemPos(int menuLevel, String menuPart) {
    List<DocumentReference> parents = webUtilsService.getDocumentParentsList(
        getContext().getDoc().getDocumentReference(), true);
    if (parents.size() >= menuLevel) {
      return getMenuItemPos(parents.get(parents.size() - menuLevel), menuPart);
    }
    return -1;
  }

  public int getMenuItemPos(DocumentReference docRef, String menuPart) {
    try {
      DocumentReference parent = getParentRef(docRef);
      int pos = -1;
      for (TreeNode menuItem : getSubNodesForParent(parent, menuPart)) {
        pos = pos + 1;
        if (docRef.equals(menuItem.getDocumentReference())) {
          return pos;
        }
      }
    } catch (XWikiException e) {
      LOGGER.error(e);
    }
    return -1;
  }
  
  /**
   * 
   * @deprecated since 2.17.0 use getSubNodesForParent(EntityReference, INavFilter)  or 
   * getSubNodesForParent(EntityReference, String) instead
   */
  @Deprecated
  public <T> List<TreeNode> getSubNodesForParent(String parent, String menuSpace,
      INavFilter<T> filter) {
    if("".equals(menuSpace)) {
      menuSpace = getContext().getDoc().getDocumentReference().getLastSpaceReference(
          ).getName();
    }
    String parentKey = getParentKey(parent, menuSpace);
    return getSubNodesForParent(getRef(parentKey), filter);
  }

  String getParentKey(String parent, String menuSpace) {
    String parentKey = "";
    if (parent != null) {
      parentKey = parent;
    }
    if(parentKey.indexOf('.') < 0) {
      parentKey = menuSpace + "." + parentKey;
    }
    if(parentKey.indexOf(':') < 0) {
      parentKey = getContext().getDatabase() + ":" + parentKey;
    }
    return parentKey;
  }
  
  String getParentKey(EntityReference reference, boolean withDataBase) {
    String parentKey = "";
    if (reference != null) {
      parentKey = serializer.serialize(reference);
      if (!parentKey.contains(":") && !parentKey.endsWith(":")) {
        parentKey += ":";
      } else if (!parentKey.contains(".") && !parentKey.endsWith(".")) {
        parentKey += ".";
      }
      if (!withDataBase) {
        parentKey = parentKey.substring(parentKey.indexOf(":") + 1);
      }
    }
    LOGGER.debug("getParentKey: returning [" + parentKey + "] for entityref ["
        + reference + "].");
    return parentKey;
  }
  
  public <T> List<TreeNode> getSubNodesForParent(EntityReference entRef,
      INavFilter<T> filter) {
    ArrayList<TreeNode> menuArray = new ArrayList<TreeNode>();
    for(TreeNode node : fetchNodesForParentKey(entRef)) {
      if((node!=null) && filter.includeTreeNode(node, getContext())) {
        // show only Menuitems of pages accessible to the current user
        menuArray.add(node);
      }
    }
    return menuArray;
  }
  
  /**
   * 
   * @deprecated since 2.17.0 use getSubNodesForParent(EntityReference, INavFilter)  or 
   * getSubNodesForParent(EntityReference, String) instead
   */
  @Deprecated
  public List<TreeNode> getSubNodesForParent(String parent, String menuSpace,
      String menuPart) {
    InternalRightsFilter filter = new InternalRightsFilter();
    filter.setMenuPart(menuPart);
    List<TreeNode> subNodesForParent = getSubNodesForParent(parent, menuSpace, filter);
    LOGGER.debug("getSubNodesForParent deprecated use: parent [" + parent
        + "] menuSpace [" + menuSpace + "] menuPart [" + menuPart + "] returning ["
        + subNodesForParent.size() + "].");
    return subNodesForParent;
  }
  
  public List<TreeNode> getSubNodesForParent(EntityReference entRef, String menuPart) {
    InternalRightsFilter filter = new InternalRightsFilter();
    filter.setMenuPart(menuPart);
    return getSubNodesForParent(entRef, filter);
  }

  /**
   * fetchNodesForParentKey
   * @param parentKey
   * @param context
   * @return Collection keeps ordering of TreeNodes according to posId
   */
  List<TreeNode> fetchNodesForParentKey(EntityReference reference) {
    String parentKey = getParentKey(reference, false);
{'fraction_non_alphanumeric': 0.10529442340536126, 'fraction_numerical': 0.004158312913046707, 'mean_word_length': 3.904588492352513, 'pattern_counts': {'":': 10, '<': 38, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3872907', 'n_tokens_mistral': 3940, 'n_tokens_neox': 3862, 'n_words': 965}