View Javadoc

1   /*
2    * MenuRepository.java
3    *
4    * Created on January 29, 2001, 9:51 AM
5    */
6   package net.sf.navigator.menu;
7   
8   import net.sf.navigator.displayer.MenuDisplayerMapping;
9   import net.sf.navigator.displayer.VelocityMenuDisplayer;
10  import net.sf.navigator.util.LoadableResource;
11  import net.sf.navigator.util.LoadableResourceException;
12  import org.apache.commons.collections.map.LinkedMap;
13  import org.apache.commons.digester.Digester;
14  import org.apache.commons.logging.Log;
15  import org.apache.commons.logging.LogFactory;
16  
17  import javax.servlet.ServletContext;
18  import java.io.InputStream;
19  import java.io.Serializable;
20  import java.util.*;
21  
22  /**
23   * Holder of Menus and their items. Can be populated programmatically.
24   *
25   * @author  ssayles, mraible
26   */
27  public class MenuRepository implements LoadableResource, Serializable {
28      //~ Static fields/initializers =============================================
29  
30      public static final String MENU_REPOSITORY_KEY = "net.sf.navigator.menu.MENU_REPOSITORY";
31      private static Log log = LogFactory.getLog(MenuRepository.class);
32  
33      //~ Instance fields ========================================================
34  
35      protected String config = null;
36      protected String name = null;
37      protected ServletContext servletContext = null;
38      protected LinkedMap menus = new LinkedMap();
39      protected LinkedMap displayers = new LinkedMap();
40      protected LinkedMap templates = new LinkedMap();
41      private String breadCrumbDelimiter;
42  
43      //~ Methods ================================================================
44      public Set getMenuNames() {
45          return menus.keySet();
46      }
47  
48      /**
49       * Convenience method for dynamic menus - returns the top-level menus
50       * only.
51       */
52      public List getTopMenus() {
53          List topMenus = new ArrayList();
54          if (menus == null) {
55              log.warn("No menus found in repository!");
56              return topMenus;
57          }
58  
59          for (Iterator it = menus.keySet().iterator(); it.hasNext();) {
60              String name = (String) it.next();
61              MenuComponent menu = getMenu(name);
62              if (menu.getParent() == null) {
63                  topMenus.add(menu);
64              }
65          }
66          return topMenus;
67      }
68  
69      public MenuComponent getMenu(String menuName) {
70          return (MenuComponent) menus.get(menuName);
71      }
72  
73      public MenuDisplayerMapping getMenuDisplayerMapping(String displayerName) {
74          return (MenuDisplayerMapping) displayers.get(displayerName);
75      }
76  
77      protected Digester initDigester() {
78          Digester digester = new Digester();
79          digester.setClassLoader(Thread.currentThread().getContextClassLoader());
80          digester.push(this);
81  
82          //digester.setDebug(getDebug());
83          // 1
84          digester.addObjectCreate("MenuConfig/Menus/Menu", "net.sf.navigator.menu.MenuComponent", "type");
85          digester.addSetProperties("MenuConfig/Menus/Menu");
86          digester.addSetNext("MenuConfig/Menus/Menu", "addMenu");
87  
88          // 2
89          digester.addObjectCreate("MenuConfig/Menus/Menu/Item", "net.sf.navigator.menu.MenuComponent", "type");
90          digester.addSetProperties("MenuConfig/Menus/Menu/Item");
91          digester.addSetNext("MenuConfig/Menus/Menu/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
92  
93          // 3        
94          digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item", "net.sf.navigator.menu.MenuComponent", "type");
95          digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item");
96          digester.addSetNext("MenuConfig/Menus/Menu/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
97  
98          // 4
99          digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type");
100         digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item");
101         digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
102 
103         // 5
104         digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type");
105         digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item");
106         digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
107 
108         // 6
109         digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type");
110         digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item/Item");
111         digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
112 
113         // 7
114         digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type");
115         digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item/Item/Item");
116         digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent");
117 
118         digester.addObjectCreate("MenuConfig/Displayers/Displayer", "net.sf.navigator.displayer.MenuDisplayerMapping", "mapping");
119         digester.addSetProperties("MenuConfig/Displayers/Displayer");
120         digester.addSetNext("MenuConfig/Displayers/Displayer", "addMenuDisplayerMapping", "net.sf.navigator.displayer.MenuDisplayerMapping");
121         digester.addSetProperty("MenuConfig/Displayers/Displayer/SetProperty", "property", "value");
122 
123         return digester;
124     }
125 
126     /**
127      * Adds a new menu.  This is called when parsing the menu xml definition.
128      * @param menu The menu component to add.
129      */
130     public void addMenu(MenuComponent menu) {
131         if (menus.containsKey(menu.getName())) {            
132             if (log.isDebugEnabled()) {
133                 log.warn("Menu '" + menu.getName() + "' already exists in repository");
134             }
135             List children = (getMenu(menu.getName())).getComponents();
136             if (children != null && menu.getComponents() != null) {
137                 for (Iterator it = children.iterator(); it.hasNext();) {
138                     MenuComponent child = (MenuComponent) it.next();
139                     menu.addMenuComponent(child);
140                 }
141             }
142         }
143         menus.put(menu.getName(), menu);
144     }
145 
146     /**
147      * Allows easy removal of a menu by its name.
148      * @param name
149      */
150     public void removeMenu(String name) {
151         menus.remove(name);
152     }
153 
154     /**
155      * Allows easy removal of all menus, suggested use for users wanting to reload menus without having to perform
156      * a complete reload of the MenuRepository
157      */
158     public void removeAllMenus() {
159         menus.clear();
160     }
161 
162     public void addMenuDisplayerMapping(MenuDisplayerMapping displayerMapping) {
163         displayers.put(displayerMapping.getName(), displayerMapping);
164         if (displayerMapping.getType().equals("net.sf.navigator.displayer.VelocityMenuDisplayer")) {
165             if (servletContext == null) {
166                 log.error("ServletContext not set - can't initialize Velocity");
167             } else {
168                 VelocityMenuDisplayer.initialize(servletContext);
169             }
170         }
171     }
172 
173     /**
174      * This method is so menu repositories can retrieve displayers from the
175      * default repository specified in menu-config.xml
176      * @return the displayers specified in this repository
177      */
178     public LinkedMap getDisplayers() {
179         return displayers;
180     }
181 
182     /**
183      * Allow the displayers to be set as a whole.  This should only be used
184      * when copying the displayers from the default repository to a newly
185      * created repository.
186      * @param displayers
187      */
188     public void setDisplayers(LinkedMap displayers) {
189         this.displayers = displayers;
190     }
191 
192     public void load() throws LoadableResourceException {
193         if (getServletContext() == null) {
194             throw new LoadableResourceException("no reference to servlet context found");
195         }
196 
197         InputStream input = null;
198         Digester digester = initDigester();
199 
200         try {
201             input = getServletContext().getResourceAsStream(config);
202             digester.parse(input);
203         } catch (Exception e) {
204             e.printStackTrace();
205             throw new LoadableResourceException("Error parsing resource file: " + config + " nested exception is: " + e.getMessage());
206         } finally {
207             try {
208                 input.close();
209             } catch (Exception e) {
210             }
211         }
212     }
213 
214     public void reload() throws LoadableResourceException {
215         menus.clear();
216         displayers.clear();
217         load();
218     }
219 
220     public void setLoadParam(String loadParam) {
221         config = loadParam;
222     }
223 
224     public String getLoadParam() {
225         return config;
226     }
227 
228     public void setName(String name) {
229         this.name = name;
230     }
231 
232     public String getName() {
233         return name;
234     }
235 
236     public ServletContext getServletContext() {
237         return servletContext;
238     }
239 
240     public void setServletContext(ServletContext context) {
241         this.servletContext = context;
242     }
243 
244     /**
245      * Method getMenu.  Get a subMenu beneath a root or parent menu.  Will drill-down as deep as requested
246      * @param menuName - e.g grandParent.parent.menu
247      * @param delimiter - e.g. '.'
248      * @return MenuComponent
249      */
250     public MenuComponent getMenu(String menuName, String delimiter) {
251         MenuComponent parent = null;
252         StringTokenizer st = new StringTokenizer(menuName, delimiter);
253         boolean firstMenu = true;
254 
255         while (st.hasMoreTokens()) {
256             if (firstMenu) {
257                 parent = this.getMenu(st.nextToken());
258                 firstMenu = false;
259             } else {
260                 MenuComponent child = null;
261                 String name = st.nextToken();
262                 for (int a = 0; a < parent.getComponents().size(); a++) {
263                     if (name.equals(((MenuComponent) parent.getComponents().get(a)).getName())) {
264                         child = (MenuComponent) parent.getComponents().get(a);
265                         a = parent.getComponents().size();
266                     }
267                 }
268                 if (child != null) {
269                     parent = child;
270                 } else {
271                     parent = null;
272                     break;
273                 }
274             }
275         }
276 
277         return parent;
278     }
279 
280     /**
281      * Method getMenuDepth.
282      * Get the depth of the deepest sub-menu within the requested top menu
283      * @param menuName - name of the top menu to check the menu depth 
284      * @return int.  If no menuName found return -1
285      */
286     public int getMenuDepth(String menuName) {
287 
288         MenuComponent menu = this.getMenu(menuName);
289         if (menu == null)
290             return -1;
291         if (menu.getMenuComponents() == null)
292             return 1;
293         return menu.getMenuDepth();
294     }
295 
296     /**
297      * Method getMenuDepth.
298      * Get the depth of the deepest sub-menu throughout all menus held in the repository
299      * @return int.  If no menus return -1.
300      */
301     public int getMenuDepth() {
302         int currentDepth = 0;
303 
304         List topMenus = this.getTopMenus();
305 
306         if (topMenus == null)
307             return -1;
308         for (Iterator menu = topMenus.iterator(); menu.hasNext();) {
309             int depth = ((MenuComponent) menu.next()).getMenuDepth();
310             if (currentDepth < depth)
311                 currentDepth = depth;
312         }
313         return currentDepth;
314     }
315 
316     /**
317      * Method getTopMenusAsArray.  Get menus as array rather than a List
318      * @return MenuComponent[]
319      */
320     public MenuComponent[] getTopMenusAsArray() {
321         List menuList = this.getTopMenus();
322         MenuComponent[] menus = new MenuComponent[menuList.size()];
323         for (int a = 0; a < menuList.size(); a++) {
324             menus[a] = (MenuComponent) menuList.get(a);
325         }
326 
327         return menus;
328     }
329 
330     /**
331      * Get a List of all the top menus' names
332      * @return List
333      */
334     public List getTopMenusNames() {
335         List menus = this.getTopMenus();
336         ArrayList names = new ArrayList();
337         for (Iterator iterator = menus.iterator(); iterator.hasNext();) {
338             MenuComponent menu = (MenuComponent) iterator.next();
339             names.add(menu.getName());
340         }
341         return names;
342     }
343 
344     public void setBreadCrumbDelimiter(String string) {
345         breadCrumbDelimiter = string;
346     }
347 
348     public void buildBreadCrumbs() {
349         if (breadCrumbDelimiter == null) {
350             throw new NullPointerException("No breadCrumbDelimiter present");
351         }
352         ArrayList menus = (ArrayList)this.getTopMenus();
353         for (Iterator iterator = menus.iterator(); iterator.hasNext();) {
354             MenuComponent menu = (MenuComponent)iterator.next();
355             menu.setBreadCrumb(breadCrumbDelimiter);
356         }
357     }
358 
359     public void buildBreadCrumbs(String delimiter) {
360         this.breadCrumbDelimiter = delimiter;
361         buildBreadCrumbs();
362     }
363 }