/* * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * 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.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()).startsWith("/") // make sure nodePath is the parent of currentPath && !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() { 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 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); } // 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); } } 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); } }