/*
* (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* 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.
*
* Contributors:
* Nuxeo - initial API and implementation
*
* $Id$
*/
package org.nuxeo.ecm.webapp.tree;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.Filter;
import org.nuxeo.ecm.core.api.SortInfo;
import org.nuxeo.ecm.core.api.Sorter;
import org.nuxeo.ecm.core.api.quota.QuotaStats;
import org.nuxeo.ecm.core.api.quota.QuotaStatsNonFolderishCount;
import org.nuxeo.ecm.core.api.security.SecurityConstants;
import org.nuxeo.ecm.core.schema.FacetNames;
import org.nuxeo.ecm.platform.contentview.jsf.ContentView;
import org.nuxeo.ecm.platform.query.api.PageProvider;
import org.nuxeo.ecm.platform.query.api.PageProviderService;
import org.nuxeo.ecm.platform.query.nxql.CoreQueryDocumentPageProvider;
import org.nuxeo.runtime.api.Framework;
/**
* Tree node of documents.
* <p>
* Children are lazy-loaded from backend only when needed.
*
* @author Anahide Tchertchian
*/
public class DocumentTreeNodeImpl implements DocumentTreeNode {
private static final long serialVersionUID = 1L;
private static final Log log = LogFactory.getLog(DocumentTreeNodeImpl.class);
protected final DocumentModel document;
protected final Filter filter;
protected final Filter leafFilter;
protected final Sorter sorter;
protected final String pageProviderName;
protected ContentView orderableContentView;
protected Map<Object, DocumentTreeNodeImpl> children;
protected Boolean expanded = null;
public DocumentTreeNodeImpl(DocumentModel document, Filter filter,
Filter leafFilter, Sorter sorter, String pageProviderName) {
this.document = document;
this.filter = filter;
this.leafFilter = leafFilter;
this.sorter = sorter;
this.pageProviderName = pageProviderName;
}
/**
* @deprecated since 5.9.1, sessionId not used.
*/
@Deprecated
public DocumentTreeNodeImpl(String sessionId, DocumentModel document,
Filter filter, Filter leafFilter, Sorter sorter,
String pageProviderName) {
this(document, filter, leafFilter, sorter, pageProviderName);
}
/**
* @deprecated since 5.9.1, sessionId not used.
*/
@Deprecated
public DocumentTreeNodeImpl(String sessionId, DocumentModel document,
Filter filter, Sorter sorter) {
this(document, filter, null, sorter, null);
}
public DocumentTreeNodeImpl(DocumentModel document, Filter filter,
Sorter sorter) {
this(document, filter, null, sorter, (String) null);
}
public List<DocumentTreeNode> getChildren() {
if (children == null) {
fetchChildren();
}
List<DocumentTreeNode> childrenNodes = new ArrayList<DocumentTreeNode>();
childrenNodes.addAll(children.values());
return childrenNodes;
}
public DocumentModel getDocument() {
return document;
}
public String getId() {
if (document != null) {
return document.getId();
}
return null;
}
public boolean isSelected(DocumentModel currentDocument) {
if (currentDocument != null) {
if (!currentDocument.isFolder()) {
// check if it's the closest parent
String currentPath = currentDocument.getPathAsString();
String nodePath = getPath();
if (currentPath != null
&& nodePath != null
&& currentPath.startsWith(nodePath)
&& currentPath.length() > nodePath.length()
&& !currentPath.substring(nodePath.length() + 1).contains(
"/")) {
// direct parent
return true;
}
} else {
// check equality
return currentDocument.getId().equals(getId());
}
}
return false;
}
public String getPath() {
if (document != null) {
return document.getPathAsString();
}
return null;
}
/**
* Resets children map
*/
public void resetChildren() {
children = null;
}
@SuppressWarnings("unchecked")
public void fetchChildren() {
try {
children = new LinkedHashMap<Object, DocumentTreeNodeImpl>();
if (leafFilter != null && leafFilter.accept(document)) {
// filter says this is a leaf, don't look at children
return;
}
CoreSession session = getCoreSession();
if (session == null) {
log.error("Cannot retrieve CoreSession for " + document);
return;
}
List<DocumentModel> documents;
boolean isOrderable = document.hasFacet(FacetNames.ORDERABLE);
if (pageProviderName == null) {
// get the children using the core
Sorter sorterToUse = isOrderable ? null : sorter;
documents = session.getChildren(document.getRef(), null,
SecurityConstants.READ, filter, sorterToUse);
} else {
// use page providers
try {
PageProviderService ppService = Framework.getService(PageProviderService.class);
List<SortInfo> sortInfos = null;
Map<String, Serializable> props = new HashMap<String, Serializable>();
props.put(
CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY,
(Serializable) session);
if (isOrderable) {
// override sort infos to get default sort
sortInfos = new ArrayList<SortInfo>();
sortInfos.add(new SortInfo("ecm:pos", true));
}
PageProvider<DocumentModel> pp = (PageProvider<DocumentModel>) ppService.getPageProvider(
pageProviderName, sortInfos, null, null, props,
new Object[] { getId() });
documents = pp.getCurrentPage();
documents = filterAndSort(documents, !isOrderable);
} catch (Exception e) {
throw new ClientException(e);
}
}
// build the children nodes
for (DocumentModel child : documents) {
String identifier = child.getId();
DocumentTreeNodeImpl childNode;
childNode = new DocumentTreeNodeImpl(child, filter, leafFilter,
sorter, pageProviderName);
children.put(identifier, childNode);
}
} catch (ClientException e) {
log.error(e);
}
}
protected CoreSession getCoreSession() {
CoreSession session = document.getCoreSession();
if (session == null) {
session = (CoreSession) Component.getInstance("documentManager",
ScopeType.CONVERSATION);
}
return session;
}
protected List<DocumentModel> filterAndSort(List<DocumentModel> docs,
boolean doSort) {
// filter and sort if defined
List<DocumentModel> res = new ArrayList<DocumentModel>();
if (docs != null) {
if (filter == null) {
res.addAll(docs);
} else {
for (DocumentModel doc : docs) {
if (filter.accept(doc)) {
res.add(doc);
}
}
}
}
if (sorter != null && doSort) {
Collections.sort(res, sorter);
}
return res;
}
@Override
public QuotaStats getQuotaStats() {
return document != null ? document.getAdapter(QuotaStatsNonFolderishCount.class)
: null;
}
@Override
public boolean isExpanded() {
if (expanded == null) {
final TreeActions treeActionBean = (TreeActionsBean) Component.getInstance("treeActions");
if (!treeActionBean.isNodeExpandEvent()) {
String currentDocPath = treeActionBean.getCurrentDocumentPath();
if (currentDocPath != null && getPath() != null
&& currentDocPath.startsWith(getPath())) {
// additional slower check for strict path prefix
if ((currentDocPath + '/').startsWith(getPath() + '/')
|| "/".equals(getPath())) {
expanded = Boolean.TRUE;
}
}
}
}
return Boolean.TRUE.equals(expanded);
}
@Override
public void setExpanded(boolean expanded) {
this.expanded = Boolean.valueOf(expanded);
}
}