/* * gvNIX is an open source tool for rapid application development (RAD). * Copyright (C) 2010 Generalitat Valenciana * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * 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 General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package __TOP_LEVEL_PACKAGE__.web.menu; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.context.ApplicationContextException; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationObjectSupport; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Menu model loader and publisher. * <p> * This class automatically is loaded by Spring ({@link Component} and * {@link Configurable}). * <p> * In application initialization ({@link WebApplicationObjectSupport}) loads * the menu structure and put it in the application context ready for * rendering. Then {@link ContextMenuStrategy} and security will decide which * section and items have to be shown. * * @author <a href="http://www.disid.com">DISID Corporation S.L.</a> made for * <a href="http://www.dgti.gva.es">General Directorate for Information Technologies (DGTI)</a> */ @Component @Configurable public class MenuLoader extends WebApplicationObjectSupport { /** Configuration file for menus */ public static final String MENU_CONFIG_FILE = "/WEB-INF/views/menu.xml"; /** Key to store Menu in application context */ public static final String MENU_SERVLET_CONTEXT_KEY = "gvnixMenu"; public static final String MENU_ITEM_ELEMENT = "menu-item"; /** * Calls to {@link #loadMenu()} and set the menu to the {@link ServletContext} */ protected void initApplicationContext() throws ApplicationContextException { if (!(getApplicationContext() instanceof WebApplicationContext)) { return; } Menu menu; try { menu = loadMenu(); } catch (Exception e) { throw new ApplicationContextException("Error loading gvNIX web menu", e); } getServletContext().setAttribute(MENU_SERVLET_CONTEXT_KEY, menu); } /** * Loads menu model from XML file {@link #MENU_CONFIG_FILE}. This method loads * menu using recursivity. Override this * method to change menu's source. Remember to set to <code>null</code> * {@link #MENU_CONFIG_FILE}. * * @return * @throws Exception */ protected Menu loadMenu() throws Exception { // load and parse menu.xml DocumentBuilder db = DocumentBuilderFactory.newInstance() .newDocumentBuilder(); Document xml = null; InputStream input = getServletContext().getResourceAsStream(MENU_CONFIG_FILE); Assert.notNull(input, "gvNIX menu configuration not found '" .concat(MENU_CONFIG_FILE).concat("'")); try { xml = db.parse(input); } catch (Exception e) { throw e; } finally { input.close(); } // create menu root Element root = xml.getDocumentElement(); String menuId = root.getAttribute("id"); Menu menu = new Menu(menuId); // parse children NodeList childNodes = root.getChildNodes(); // return empty menu if there are no children if(childNodes.getLength() == 0) { return menu; } // load root menu items and their children List<MenuItem> childItems = loadMenuItems(childNodes); menu.setChildren(childItems); return menu; } /** * Transform a list of {@code <menu-item>} elements into {@code MenuItem} * objects. * <p> * This method loads the children recursively. * * @param nodes List of XML elements * @return List of MenuItem, note that MenuItem is complete, that is * each MenuItem will contain their children List */ public List<MenuItem> loadMenuItems(NodeList nodes) { List<MenuItem> items = new ArrayList<MenuItem>(); for(int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() != Node.ELEMENT_NODE || !MENU_ITEM_ELEMENT.equals(node.getNodeName())) { continue; } // create the MenuItem object MenuItem item = new MenuItem((Element) node); // recursively load children NodeList childNodes = node.getChildNodes(); if(childNodes.getLength() > 0) { List<MenuItem> childItems = loadMenuItems(childNodes); item.setChildren(childItems); } // add MenuItem to result list items.add(item); } return items; } }