Java tutorial
/** * Copyright (C) 2000 - 2012 Silverpeas * * This program 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. * * As a special exception to the terms and conditions of version 3.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * Open Source Software ("FLOSS") applications as described in Silverpeas's * FLOSS exception. You should have received a copy of the text describing * the FLOSS exception, and it is also available here: * "http://www.silverpeas.org/legal/licensing" * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.silverpeas.tags.navigation; import java.io.IOException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import javax.servlet.ServletException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; import org.apache.commons.lang.StringEscapeUtils; import com.silverpeas.tags.kmelia.KmeliaTagUtil; import com.silverpeas.tags.navigation.config.Configurateur; import com.silverpeas.tags.navigation.links.LinkGeneratorFactory; import com.silverpeas.tags.navigation.utils.NodeDetailComparator; import com.silverpeas.tags.pdc.PdcTagUtil; import com.stratelia.silverpeas.pdc.model.Value; import com.stratelia.webactiv.util.node.model.NodeDetail; import com.stratelia.webactiv.util.publication.model.PublicationDetail; /** * Tag permettant de gnrer la structure de l'arborescence du menu. * @author svuillet */ public class MenuTag extends TagSupport { private static final long serialVersionUID = 7316128024807549206L; private static final String TOPIC_ID_PREFIX = "topicId-"; private static final String PARENT_TOPIC_ID_PREFIX = "parentTopicId-"; private KmeliaTagUtil themetracker = null; private PdcTagUtil pdc = new PdcTagUtil(null, null, 0, null); private String idTopicRoot; private String idTopicSubRoot = null; private String selectedTopicIdParameterName = null; private String selectedTopicNameInSession = null; private String id; private String classNamesHierarchy; private String maxDeepLevel = null; private String excludeTopicsNamed = null; private String idAxisFiltering = null; private String axisValueFilter = null; private String prefixIdHierarchy = null; private String classNamesHierarchyFiltered; private String classNameSeparator = null; private boolean hierarchicSelection = true; /** * Prfixe la classe css par "selected" de l'item selectionn et eventuellement de ses items * parents. * @param hierarchicSelection */ public void setHierarchicSelection(String hierarchicSelection) { this.hierarchicSelection = new Boolean(hierarchicSelection); } /** * Nom des thmes qui seront exclus du menu. * @param excludeTopicsNamed */ public void setExcludeTopicsNamed(String excludeTopicsNamed) { this.excludeTopicsNamed = excludeTopicsNamed; } /** * Prefixes des ids des topics dans l'url gnre. * @param prefixIdHierarchy */ public void setPrefixIdHierarchy(String prefixIdHierarchy) { this.prefixIdHierarchy = prefixIdHierarchy; } /** * Nom de la classe CSS de sparation des items du niveau 1. * @param classNameSeparator */ public void setClassNameSeparator(String classNameSeparator) { this.classNameSeparator = classNameSeparator; } /** * Id de l'axe qui va servir pour filtrer l'arborescence. * @param idAxisFiltering */ public void setIdAxisFiltering(String idAxisFiltering) { this.idAxisFiltering = idAxisFiltering; } /** * Valeur du filtre de l'arborescence. * @param axisValueFilter */ public void setAxisValueFilter(String axisValueFilter) { this.axisValueFilter = axisValueFilter; } /** * Classes CSS alternative aux classes de l'attribut "ClassNamesHierarchy". Ces classes sont * appliques si des lments du niveau courant sont positionns sur idAxisFiltering avec la * valeurs "axisValueFilter". * @param classNamesHierarchyFiltered */ public void setClassNamesHierarchyFiltered(String classNamesHierarchyFiltered) { this.classNamesHierarchyFiltered = classNamesHierarchyFiltered; } /** * Point de dmarrage du parcours de l'arborescence. * @param idTopicRoot */ public void setIdTopicRoot(String idTopicRoot) { this.idTopicRoot = idTopicRoot; } /** * Point de dmarrage de l'affichage de l'arborescence. * @param idTopicSubRoot */ public void setIdTopicSubRoot(String idTopicSubRoot) { this.idTopicSubRoot = idTopicSubRoot; } /** * Limite du parcours de l'arborescence. * @param maxDeepLevel */ public void setMaxDeepLevel(String maxDeepLevel) { this.maxDeepLevel = maxDeepLevel; } /** * Source de donnes. * @param tt */ public void setThemetracker(String tt) { int scope = pageContext.getAttributesScope(tt); themetracker = (KmeliaTagUtil) pageContext.getAttribute(tt, scope); } /** * Nom du parametre dans l'url qui contient l'id du topic selectionn. * @param selectedTopicIdParameterName */ public void setSelectedTopicIdParameterName(String selectedTopicIdParameterName) { this.selectedTopicIdParameterName = selectedTopicIdParameterName; } /** * Nom de l'attribut stoqu en session qui contient le topic selectionn. * @param selectedTopicNameInSession */ public void setSelectedTopicNameInSession(String selectedTopicNameInSession) { this.selectedTopicNameInSession = selectedTopicNameInSession; } /** * Id de l'lment HTML contenant le menu (UL). */ public void setId(String id) { this.id = id; } /** * Noms des classes CSS (spars par des virgules) appliquer chaque niveau de l'arborescence. * Si le nombre de classes CSS est infrieur la profondeur de l'arborescence, alors la dernires * classes CSS est applique au niveau infrieurs. * @param classNamesHierarchy */ public void setClassNamesHierarchy(String classNamesHierarchy) { this.classNamesHierarchy = classNamesHierarchy; } /** * Retourne un prefix avant l'id d'un lment par rapport son niveau hierarchique. * @param theme * @return * @throws Exception */ private String getPrefixIdByLevel(NodeDetail theme) throws Exception { if (prefixIdHierarchy != null) { // selection du bon prefix appliquer int rootLevel = KmeliaCaching.getInstance(themetracker).getTopic(Integer.valueOf(idTopicRoot)) .getLevel(); int level = theme.getLevel() - rootLevel; StringTokenizer tokenizer = null; tokenizer = new StringTokenizer(prefixIdHierarchy, ","); int l = 1; String prefix = null; while (tokenizer.hasMoreTokens()) { prefix = tokenizer.nextToken(); if (level == l) { return prefix; } l++; } } return null; } /** * Retourne le nom de la classe css appliquer pour un niveau hierarchique donn. * @param level * @return * @throws Exception */ private String getClassNameByLevel(NodeDetail theme) throws Exception { boolean useAlternateClassNamesHierarchy = false; // gestion du filtrage if (idAxisFiltering != null && axisValueFilter != null && !idAxisFiltering.isEmpty() && !axisValueFilter.isEmpty()) { @SuppressWarnings("unchecked") Collection<PublicationDetail> pubs = themetracker.getPublicationsByTopic(String.valueOf(theme.getId())); Iterator<PublicationDetail> iPubs = pubs.iterator(); while (iPubs.hasNext()) { PublicationDetail pub = (PublicationDetail) iPubs.next(); @SuppressWarnings("unchecked") Collection<Value> values = pdc.getValuesOnAxis( pub.getId() + "," + themetracker.getComponentInst().getId() + "," + idAxisFiltering); Iterator<Value> iValues = values.iterator(); while (iValues.hasNext()) { Value v = (Value) iValues.next(); if (v.getName().equals(axisValueFilter)) { useAlternateClassNamesHierarchy = true; break; } } if (useAlternateClassNamesHierarchy) break; } } // selection de la bonne classe css appliquer int rootLevel = KmeliaCaching.getInstance(themetracker).getTopic(Integer.valueOf(idTopicRoot)).getLevel(); int level = theme.getLevel() - rootLevel; StringTokenizer tokenizer = null; if (useAlternateClassNamesHierarchy) { tokenizer = new StringTokenizer(classNamesHierarchyFiltered, ","); } else { tokenizer = new StringTokenizer(classNamesHierarchy, ","); } int l = 1; String className = null; while (tokenizer.hasMoreTokens()) { className = tokenizer.nextToken(); if (level == l) { return getFinalClassName(theme, className); } l++; } return getFinalClassName(theme, className); } /** * Retourne le nom de la class css appliquer en fonction de la selection des lments. * @param theme * @param className * @return */ private String getFinalClassName(NodeDetail theme, String className) throws RemoteException, IOException { if (isSelectedItem(theme)) { if (selectedTopicNameInSession != null) { pageContext.getSession().setAttribute(selectedTopicNameInSession, theme); } return "selected-" + className; } else { return className; } } /** * Test si l'lment du menu est selectionn. * @param topicId * @return */ private boolean isSelectedItem(NodeDetail theme) throws RemoteException, IOException { if (selectedTopicNameInSession != null) { NodeDetail node = (NodeDetail) pageContext.getSession().getAttribute(selectedTopicNameInSession); if (node == null) return false; String selectedTopicId = String.valueOf(node.getId()); return isInSelectionPath(selectedTopicId, theme); } if (selectedTopicIdParameterName != null) { String selectedTopicId = pageContext.getRequest().getParameter(selectedTopicIdParameterName); if (selectedTopicId != null) { return isInSelectionPath(selectedTopicId, theme); } } return false; } /** * Test si un item est dans l'arborescence de selection. * @param selectedTopicId * @param currentNode * @return * @throws RemoteException */ private boolean isInSelectionPath(String selectedTopicId, NodeDetail currentNode) throws RemoteException { if (hierarchicSelection) { String selectedTopicsIds = KmeliaCaching.getInstance(themetracker) .getTopic(Integer.valueOf(selectedTopicId)).getFullPath(); StringTokenizer tokenizer = new StringTokenizer(selectedTopicsIds, "/"); while (tokenizer.hasMoreTokens()) { String nodeId = tokenizer.nextToken(); if (String.valueOf(currentNode.getId()).equals(nodeId)) { return true; } } } else { return selectedTopicId.equals(String.valueOf(currentNode.getId())); } return false; } /** * Construction d'un id html pour un theme. * @param theme * @return */ private String buildId(String prefix, NodeDetail theme) { String genericId = prefix + theme.getId(); String specificId = Configurateur.getConfigValue(genericId); if (specificId != null) { return specificId; } return genericId; } @Override public int doStartTag() throws JspException { try { JspWriter out = pageContext.getOut(); NodeDetail root = KmeliaCaching.getInstance(themetracker).getTopic(Integer.valueOf(idTopicRoot)); boolean display = true; if (idTopicSubRoot != null) { display = idTopicSubRoot.equalsIgnoreCase(idTopicRoot); } browse(out, root, 1, display); } catch (Exception e) { e.printStackTrace(); } return SKIP_BODY; } /** * Construction des onglets dans le menu. * @param out * @param rootTopic * @param level */ private void browse(JspWriter out, NodeDetail rootTopic, int level, boolean display) { try { List<NodeDetail> themes = getSubTopics(themetracker, rootTopic.getId()); Iterator<NodeDetail> iTheme = themes.iterator(); if (!themes.isEmpty()) { if (level == 1) { print(out, "<ul id='" + id + "'>", display); } else { print(out, "<ul id='" + buildId(PARENT_TOPIC_ID_PREFIX, rootTopic) + "'>", display); } } StringBuffer html = new StringBuffer(); while (iTheme.hasNext()) { NodeDetail theme = (NodeDetail) iTheme.next(); if (excludeTopicsNamed == null || theme.getName().equalsIgnoreCase(excludeTopicsNamed) == false) { html.setLength(0); html.append("<li id='"); html.append(buildId(TOPIC_ID_PREFIX, theme)); html.append("' class='"); html.append(getClassNameByLevel(theme)); html.append("'>"); html.append("<a href='"); html.append(generateFullSemanticPath(theme, getPrefixIdByLevel(theme))); html.append("' title='"); html.append(StringEscapeUtils.escapeHtml(theme.getDescription())); html.append("'><span>"); html.append(theme.getName()); html.append("</span></a>"); print(out, html.toString(), display); if ((maxDeepLevel != null && Integer.valueOf(maxDeepLevel) > level) || maxDeepLevel == null) { if (display) { browse(out, theme, level + 1, true); } else { boolean d = idTopicSubRoot.equalsIgnoreCase(String.valueOf(rootTopic.getId())) || idTopicSubRoot.equalsIgnoreCase(String.valueOf(theme.getId())); browse(out, theme, level + 1, d); } } print(out, "</li>", display); if (classNameSeparator != null && level == 1 && iTheme.hasNext()) print(out, "<li class='" + classNameSeparator + "'></li>", display); } } if (!themes.isEmpty()) { print(out, "</ul>", display); } } catch (Exception e) { e.printStackTrace(); } } /** * Print html. * @param out * @param html * @param display */ private void print(JspWriter out, String html, boolean display) { if (display) { try { out.println(html); } catch (IOException e) { e.printStackTrace(); } } } /** * Construction de l'url de l'item. * @param node * @param prefixId * @return * @throws RemoteException */ private String generateFullSemanticPath(NodeDetail node, String prefixId) throws RemoteException { try { return LinkGeneratorFactory.getInstance().newLinkGenerator().generateFullSemanticPath(pageContext, themetracker, node, idTopicRoot, null, prefixId); } catch (Exception e) { throw new RemoteException("", e); } } /** * Liste des thmes au niveau n-1. * @param themetracker * @param topicId * @return * @throws RemoteException */ private List<NodeDetail> getSubTopics(KmeliaTagUtil themetracker, int topicId) throws RemoteException { Collection<NodeDetail> arbo = KmeliaCaching.getInstance(themetracker).getTreeView(idTopicRoot); // Calcul du niveau du topic racine Iterator<NodeDetail> i = arbo.iterator(); NodeDetail n = null; while (i.hasNext()) { n = (NodeDetail) i.next(); if (n.getId() == Integer.valueOf(topicId)) { break; } } int level = n.getLevel() + 1; // Constitution de la liste des sous topics List<NodeDetail> result = new ArrayList<NodeDetail>(); i = arbo.iterator(); while (i.hasNext()) { n = (NodeDetail) i.next(); if (n.getLevel() == level && n.getFatherPK().getId().equals(String.valueOf(topicId))) { result.add(n); } } Collections.sort(result, new NodeDetailComparator()); return result; } }