/* * (C) Copyright 2010 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 * */ package org.nuxeo.ecm.webapp.directory; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.faces.context.FacesContext; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.schema.SchemaManager; import org.nuxeo.ecm.core.schema.types.Schema; import org.nuxeo.ecm.directory.DirectoryException; import org.nuxeo.ecm.directory.Session; import org.nuxeo.ecm.directory.api.DirectoryService; import org.nuxeo.ecm.platform.ui.web.directory.DirectoryHelper; import org.nuxeo.runtime.api.Framework; /** * A vocabulary tree node based on l10nvocabulary or l10nxvocabulary directory. * These schemas store translations in columns of the form label_xx_XX or * label_xx. The label of a node is retrieved from column label_xx_XX (where * xx_XX is the current locale name) if it exists, from column label_xx (where * xx is the current locale language) else. If this one doesn't exist either, * the english label (from label_en) is used. * * @since 5.5 * @author <a href="mailto:qlamerand@nuxeo.com">Quentin Lamerand</a> */ public class VocabularyTreeNode { private static final Log log = LogFactory.getLog(VocabularyTreeNode.class); public static final String PARENT_FIELD_ID = "parent"; public static final String LABEL_FIELD_PREFIX = "label_"; public static final String DEFAULT_LANGUAGE = "en"; public static final String OBSOLETE_FIELD = "obsolete"; protected final String path; protected final int level; protected String id; protected String label; protected DirectoryService directoryService; protected List<VocabularyTreeNode> children; protected String vocabularyName; protected DocumentModelList childrenEntries; protected boolean displayObsoleteEntries; protected String orderingField; protected Comparable orderingValue; protected char keySeparator; public VocabularyTreeNode(int level, String id, String description, String path, String vocabularyName, DirectoryService directoryService) { this(level, id, description, path, vocabularyName, directoryService, false, '/', null); } public VocabularyTreeNode(int level, String id, String description, String path, String vocabularyName, DirectoryService directoryService, boolean displayObsoleteEntries, char keySeparator, String orderingField) { this(level, id, description, path, vocabularyName, directoryService, displayObsoleteEntries, keySeparator, orderingField, null); } public VocabularyTreeNode(int level, String id, String description, String path, String vocabularyName, DirectoryService directoryService, boolean displayObsoleteEntries, char keySeparator, String orderingField, Comparable orderingValue) { this.level = level; this.id = id; this.label = description; this.path = path; this.vocabularyName = vocabularyName; this.directoryService = directoryService; this.displayObsoleteEntries = displayObsoleteEntries; this.keySeparator = keySeparator; this.orderingField = orderingField; this.orderingValue = orderingValue; } public List<VocabularyTreeNode> getChildren() { if (children != null) { return children; } children = new ArrayList<VocabularyTreeNode>(); try { String schemaName = getDirectorySchema(); DocumentModelList results = getChildrenEntries(); Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale(); for (DocumentModel result : results) { String childIdendifier = result.getId(); String childLabel = computeLabel(locale, result, schemaName); String childPath; if ("".equals(path)) { childPath = childIdendifier; } else { childPath = path + keySeparator + childIdendifier; } Comparable orderingValue = null; if (!StringUtils.isBlank(orderingField)) { orderingValue = (Comparable) result.getProperty(schemaName, orderingField); } children.add(new VocabularyTreeNode(level + 1, childIdendifier, childLabel, childPath, vocabularyName, getDirectoryService(), displayObsoleteEntries, keySeparator, orderingField, orderingValue)); } // sort children Comparator<? super VocabularyTreeNode> cmp; if (StringUtils.isBlank(orderingField) || "label".equals(orderingField)) { cmp = new LabelComparator(); // sort alphabetically } else { cmp = new OrderingComparator(); } Collections.sort(children, cmp); return children; } catch (ClientException e) { log.error(e); return children; } } public static String computeLabel(Locale locale, DocumentModel entry, String schemaName) { String fieldName = LABEL_FIELD_PREFIX + locale.toString(); String label = null; try { label = (String) entry.getProperty(schemaName, fieldName); } catch (Exception e) { } if (label == null) { fieldName = LABEL_FIELD_PREFIX + locale.getLanguage(); try { label = (String) entry.getProperty(schemaName, fieldName); } catch (Exception e) { } } if (label == null) { fieldName = LABEL_FIELD_PREFIX + DEFAULT_LANGUAGE; try { label = (String) entry.getProperty(schemaName, fieldName); } catch (Exception e) { } } return label; } private class LabelComparator implements Comparator<VocabularyTreeNode> { @Override public int compare(VocabularyTreeNode o1, VocabularyTreeNode o2) { return ObjectUtils.compare(o1.getLabel(), o2.getLabel()); } } private class OrderingComparator implements Comparator<VocabularyTreeNode> { @Override public int compare(VocabularyTreeNode o1, VocabularyTreeNode o2) { if (o1.getOrdering() == null && o2.getOrdering() != null) { return -1; } else if (o1.getOrdering() != null && o2.getOrdering() == null) { return 1; } else if (o1.getOrdering() == o2.getOrdering()) { return 0; } else { return o1.getOrdering().compareTo(o2.getOrdering()); } } } protected DocumentModelList getChildrenEntries() throws ClientException { if (childrenEntries != null) { // memorized directory lookup since directory content is not // suppose to change // XXX: use the cache manager instead of field caching strategy return childrenEntries; } Session session = getDirectorySession(); try { Map<String, Serializable> filter = new HashMap<String, Serializable>(); String directorySchema = getDirectorySchema(); SchemaManager schemaManager = Framework.getLocalService(SchemaManager.class); Schema schema = schemaManager.getSchema(directorySchema); if (schema == null) { throw new DirectoryException(directorySchema + " is not a registered directory"); } if (level == 0 && schema.hasField(PARENT_FIELD_ID)) { // filter on empty parent filter.put(PARENT_FIELD_ID, ""); } else { String[] bitsOfPath = StringUtils.split(path, keySeparator); filter.put(PARENT_FIELD_ID, bitsOfPath[level - 1]); } if (!displayObsoleteEntries) { filter.put(OBSOLETE_FIELD, Long.valueOf(0)); } if (filter.isEmpty()) { childrenEntries = session.getEntries(); } else { childrenEntries = session.query(filter); } return childrenEntries; } finally { session.close(); } } public String getId() { return id; } public String getLabel() { return label; } public String getPath() { return path; } public Comparable getOrdering() { return orderingValue; } protected DirectoryService getDirectoryService() { if (directoryService == null) { directoryService = DirectoryHelper.getDirectoryService(); } return directoryService; } protected String getDirectorySchema() throws ClientException { return getDirectoryService().getDirectorySchema(vocabularyName); } protected Session getDirectorySession() throws ClientException { return getDirectoryService().open(vocabularyName); } }