Source code: info/crossbar/state/Sitemap.java
1 /*
2 * @(#)Sitemap.java $Revision: 1.2 $ $Date: 2003/06/04 04:55:32 $
3 *
4 * Copyright 2002 by Daniel Kehoe <kehoe@fortuity.com>
5 * All Rights Reserved
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28 package info.crossbar.state;
29
30 import java.util.logging.Logger;
31 import java.util.logging.Level;
32 import java.util.*;
33
34 import java.io.InputStream;
35
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.ServletException;
38
39 import java.sql.SQLException;
40 import java.sql.ResultSet;
41
42 import sun.jdbc.rowset.*;
43
44 import org.apache.commons.digester.*;
45
46 import info.crossbar.state.CrossbarBaseBean;
47
48 import info.crossbar.model.sitemap.Menu;
49 import info.crossbar.model.sitemap.MenuCategory;
50 import info.crossbar.model.sitemap.MenuItem;
51
52 /**
53 * Sitemap class for use by <a href="http://www.crossbar.info/">Crossbar</a>
54 *
55 * @author Daniel Kehoe, <a href="http://www.fortuity.com/">Fortuity Consulting</a>
56 * @created January 23, 2002
57 * @version <a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/crossbar/crossbar-sitemap/src/java/info/crossbar/state/Sitemap.java">View source, revision history</a>
58 * $Revision: 1.2 $ $Date: 2003/06/04 04:55:32 $
59 * <p>
60 * DESCRIPTION:
61 * Delineates a web site's structure for rendering by a navigation menu tag.
62 */
63
64 public class Sitemap extends CrossbarBaseBean {
65
66 /**
67 * Set up logging.
68 *
69 */
70 private static Logger log = Logger.getLogger(Sitemap.class.getName());
71
72 /**
73 * A look-up dictionary of menus.
74 *
75 */
76 private static HashMap menus = new HashMap();
77
78 /**
79 * A look-up dictionary of menu categories
80 *
81 */
82 private static HashMap categories = new HashMap();
83
84 /**
85 * A look-up dictionary of menu items (pages and locations)
86 *
87 */
88 private static HashMap menuItems = new HashMap();
89
90 /**
91 * By default, construct a menu defined by the "navigation" name in the
92 * sitemap.xml file.
93 *
94 */
95 private String menuName = "navigation";
96
97 /**
98 * By default, the HttpServletRequest will be null and the user
99 * will not be allowed to see any menu items meant for users in a
100 * specific role.
101 *
102 */
103 private HttpServletRequest request = null;
104
105 /**
106 * No argument Constructor.
107 *
108 */
109 public Sitemap()
110 throws SQLException {
111 log.entering(Sitemap.class.getName(), "Constructor");
112 init();
113 log.exiting(Sitemap.class.getName(), "Constructor");
114 }
115
116 /**
117 * Alternative Constructor to construct a menu other than the
118 * default "navigation" menu defined in the sitemap.xml file.
119 *
120 * @param menuNameIn a String menu name set in the sitemap.xml file
121 */
122 public Sitemap(String menuNameIn)
123 throws SQLException {
124 log.entering(Sitemap.class.getName(), "Constructor");
125 menuName = menuNameIn;
126 init();
127 log.exiting(Sitemap.class.getName(), "Constructor");
128 }
129
130 /**
131 * Alternative Constructor takes HttpServletRequest as an argument to determine
132 * which menu items the user is in a role to see.
133 *
134 * @param request HttpServletRequest needed to determine the user's role(s)
135 */
136 public Sitemap(HttpServletRequest requestIn)
137 throws SQLException {
138 log.entering(Sitemap.class.getName(), "Constructor");
139 request = requestIn;
140 init();
141 log.exiting(Sitemap.class.getName(), "Constructor");
142 }
143
144 /**
145 * Alternative Constructor to construct a menu other than the
146 * default "navigation" menu defined in the sitemap.xml file.
147 * Takes HttpServletRequest as an argument to determine
148 * which menu items the user is in a role to see.
149 *
150 * @param menuNameIn a String menu name set in the sitemap.xml file
151 * @param request HttpServletRequest needed to obtain the userID and session
152 */
153 public Sitemap(String menuNameIn, HttpServletRequest requestIn)
154 throws SQLException {
155 log.entering(Sitemap.class.getName(), "Constructor");
156 request = requestIn;
157 menuName = menuNameIn;
158 init();
159 log.exiting(Sitemap.class.getName(), "Constructor");
160 }
161
162 /**
163 * Obtain data somewhere and create a CachedRowSet.
164 *
165 */
166 public void init() {
167 log.entering(Sitemap.class.getName(), "init",
168 "trying to generate data for a menu");
169 // parse the sitemap.xml file:
170 readXML();
171 // begin constructing menus;
172 ArrayList categories = getCategoriesForMenu(menuName);
173 if (categories == null || categories.isEmpty()) {
174 error("Could not find any categories for the menu");
175 }
176 try {
177 // create an empty CachedRowSet:
178 this.release(); // clear any previous records
179 this.setTableName("Sitemap");
180 this.setType(ResultSet.TYPE_SCROLL_INSENSITIVE);
181 this.setConcurrency(ResultSet.CONCUR_UPDATABLE);
182 // create metadata:
183 RowSetMetaDataImpl meta = new RowSetMetaDataImpl();
184 meta.setColumnCount(categories.size());
185 int col = 1;
186 String category;
187 Iterator cols = categories.iterator();
188 while (cols.hasNext()) {
189 // assign column labels
190 // (SQL metadata starts at "1", Java list starts at "0"):
191 category = (String) cols.next().toString();
192 meta.setColumnName(col, category);
193 meta.setColumnLabel(col, category);
194 meta.setColumnType(col, 12);//String
195 col++;
196 }
197 // assign the metadata to the CachedRowSet:
198 this.setMetaData(meta);
199 try {
200 // iterate through each category and item to populate the CachedRowSet:
201 int maxcount = 0;
202 int colIndex = 1;
203 cols = categories.iterator(); // (consumed all so must reset)
204 while (cols.hasNext()) {
205 int i = 1;
206 category = (String) cols.next().toString();
207 ArrayList itemsList = getItemsForCategory(category);
208 if (itemsList == null || itemsList.isEmpty()) {
209 error("Could not find any items for the category \""
210 + category + "\"");
211 break;
212 }
213 Iterator items = itemsList.iterator();
214 while (items.hasNext()) {
215 String item = (String) items.next();
216 if (maxcount < i) {
217 // if no previous category had
218 // more items than this category:
219 this.afterLast();
220 this.moveToInsertRow();
221 for (int j=1; j<categories.size()+1; j++) {
222 if (j != colIndex) this.updateString(j, null);
223 }
224 this.updateString(colIndex, item);
225 this.insertRow();
226 this.moveToCurrentRow();
227 } else {
228 // if a previous category had
229 // more items than this category:
230 this.absolute(i);
231 this.updateString(colIndex, item);
232 this.updateRow();
233 }
234 if (maxcount < i) maxcount = i;
235 i++;
236 }
237 colIndex++;
238 }
239 } catch (NullPointerException cause) {
240 error("NullPointerException: " + cause);
241 } catch (ArrayIndexOutOfBoundsException cause) {
242 error("ArrayIndexOutOfBoundsException: " + cause);
243 } catch (Exception cause) {
244 error("Exception: " + cause);
245 cause.printStackTrace();
246 }
247 } catch (SQLException e) {
248 String error = e.getMessage();
249 log.warning(error);
250 } catch (Exception e) {
251 String error = e.getMessage();
252 log.warning(error);
253 e.printStackTrace();
254 }
255 log.exiting(Sitemap.class.getName(), "init");
256 return;
257 }
258
259 protected ArrayList getCategoriesForMenu(String menuName) {
260 log.entering(Sitemap.class.getName(), "getCategoriesForMenu");
261 Menu menu = (Menu) this.menus.get(menuName);
262 log.fine("got " + menu);
263 ArrayList list = menu.getItems(request, categories);
264 log.fine("categories list contains " + list.size() + " items");
265 log.exiting(Sitemap.class.getName(), "getCategoriesForMenu");
266 return list;
267 }
268
269 public String getDisplayNameOfCategory(String locale, String categoryName) {
270 log.entering(Sitemap.class.getName(), "getDisplayNameOfCategory",
271 "for category named \"" + categoryName + "\"");
272 MenuCategory menuCategory = (MenuCategory) categories.get(categoryName);
273 if (menuCategory == null) log.fine("can't find \"" + categoryName + "\"");
274 String value = menuCategory.getDisplayName(locale);
275 log.exiting(Sitemap.class.getName(), "getDisplayNameOfCategory");
276 return value;
277 }
278
279 public String getLocationOfMenuCategory(String categoryName) {
280 log.entering(Sitemap.class.getName(), "getLocationOfMenuCategory",
281 "for category named \"" + categoryName + "\"");
282 MenuCategory menuCategory = (MenuCategory) categories.get(categoryName);
283 if (menuCategory == null) log.fine("can't find \"" + categoryName + "\"");
284 String value = menuCategory.getLocation();
285 log.exiting(Sitemap.class.getName(), "getLocationOfMenuCategory");
286 return value;
287 }
288
289 protected ArrayList getItemsForCategory(String category) {
290 log.entering(Sitemap.class.getName(), "getItemsForCategory",
291 "for category named \"" + category + "\"");
292 MenuCategory menuCategory = (MenuCategory) categories.get(category);
293 if (menuCategory == null) log.fine("can't find \"" + category + "\"");
294 ArrayList list = menuCategory.getItems(request, menuItems);
295 log.fine("items list contains " + list.size() + " items");
296 log.exiting(Sitemap.class.getName(), "getItemsForCategory");
297 return list;
298 }
299
300 public String getDisplayNameOfMenuItem(String locale, String itemName) {
301 log.entering(Sitemap.class.getName(), "getDisplayNameOfMenuItem",
302 "for item named \"" + itemName + "\"");
303 MenuItem menuItem = (MenuItem) menuItems.get(itemName);
304 if (menuItem == null) log.fine("can't find \"" + itemName + "\"");
305 String value = menuItem.getDisplayName(locale);
306 log.exiting(Sitemap.class.getName(), "getDisplayNameOfMenuItem");
307 return value;
308 }
309
310 public String getLocationOfMenuItem(String itemName) {
311 log.entering(Sitemap.class.getName(), "getLocationOfMenuItem",
312 "for item named \"" + itemName + "\"");
313 MenuItem menuItem = (MenuItem) menuItems.get(itemName);
314 if (menuItem == null) log.fine("can't find \"" + itemName + "\"");
315 String value = menuItem.getLocation();
316 log.exiting(Sitemap.class.getName(), "getLocationOfMenuItem");
317 return value;
318 }
319
320 public String getDescriptionOfMenuItem(String locale, String itemName) {
321 log.entering(Sitemap.class.getName(), "getDescriptionOfMenuItem",
322 "for item named \"" + itemName + "\"");
323 MenuItem menuItem = (MenuItem) menuItems.get(itemName);
324 if (menuItem == null) log.fine("can't find \"" + itemName + "\"");
325 String value = menuItem.getDescription(locale);
326 log.exiting(Sitemap.class.getName(), "getDescriptionOfMenuItem");
327 return value;
328 }
329
330 /**
331 * Parse the sitemap.xml file to obtain data to drive a navigation menu.
332 *
333 */
334 public void readXML() {
335 log.entering(Sitemap.class.getName(), "readXML",
336 "trying to read sitemap.xml to obtain data for a menu");
337 try {
338 // reset the menus list:
339 this.menus = new HashMap();
340 // use the Jakarta Commons Digester API to read the xml file:
341 Digester digester = new Digester();
342 // This method pushes this class to the Digester's
343 // object stack making its methods available to processing rules.
344 digester.push(this);
345 // create Menu objects:
346 digester.addObjectCreate("crossbar/menu", Menu.class);
347 digester.addSetProperties("crossbar/menu", "name", "name");
348 digester.addCallMethod("crossbar/menu/display", "setDisplayName", 2);
349 digester.addCallParam("crossbar/menu/display", 0, "locale");
350 digester.addCallParam("crossbar/menu/display", 1);
351 digester.addCallMethod("crossbar/menu/categories", "setSortOrder", 2);
352 digester.addCallParam("crossbar/menu/categories", 0, "sort-order");
353 digester.addCallParam("crossbar/menu/categories", 1);
354 digester.addCallMethod("crossbar/menu/roles", "setRole", 1);
355 digester.addCallParam("crossbar/menu/roles", 0);
356 digester.addSetNext("crossbar/menu", "addMenu");
357 // create MenuCategory objects:
358 digester.addObjectCreate("crossbar/category", MenuCategory.class);
359 digester.addSetProperties("crossbar/category", "name", "name");
360 digester.addCallMethod("crossbar/category/display", "setDisplayName", 2);
361 digester.addCallParam("crossbar/category/display", 0, "locale");
362 digester.addCallParam("crossbar/category/display", 1);
363 digester.addBeanPropertySetter("crossbar/category/location", "location");
364 digester.addCallMethod("crossbar/category/pages", "setSortOrder", 2);
365 digester.addCallParam("crossbar/category/pages", 0, "sort-order");
366 digester.addCallParam("crossbar/category/pages", 1);
367 digester.addCallMethod("crossbar/category/roles", "setRole", 1);
368 digester.addCallParam("crossbar/category/roles", 0);
369 digester.addSetNext("crossbar/category", "addMenuCategory");
370 // create MenuItem objects:
371 digester.addObjectCreate("crossbar/page", MenuItem.class);
372 digester.addSetProperties("crossbar/page", "name", "name");
373 digester.addCallMethod("crossbar/page/display", "setDisplayName", 2);
374 digester.addCallParam("crossbar/page/display", 0, "locale");
375 digester.addCallParam("crossbar/page/display", 1);
376 digester.addCallMethod("crossbar/page/description", "setDescription", 2);
377 digester.addCallParam("crossbar/page/description", 0, "locale");
378 digester.addCallParam("crossbar/page/description", 1);
379 digester.addBeanPropertySetter("crossbar/page/location", "location");
380 digester.addCallMethod("crossbar/page/roles", "setRole", 1);
381 digester.addCallParam("crossbar/page/roles", 0);
382 digester.addSetNext("crossbar/page", "addMenuItem");
383 log.fine("ready to parse");
384 // This doesn't work when the app is installed from a war file:
385 // String xmlFilePath = App.context.getRealPath("/WEB-INF/sitemap.xml");
386 // Use this instead:
387 InputStream xmlFile = App.context.getResourceAsStream("/WEB-INF/sitemap.xml");
388 // This method starts the parsing of the document.
389 digester.parse(xmlFile);
390 log.fine("menus list contains " + this.menus.size() + " items");
391 } catch (Exception e) {
392 log.warning(e.getMessage());
393 e.printStackTrace();
394 }
395 log.exiting(Sitemap.class.getName(), "readXML");
396 return;
397 }
398
399 /**
400 * Invoked by the Digester.
401 *
402 */
403 public void addMenu(Menu item) {
404 log.fine("adding menu named \"" + item.getName() + "\"");
405 this.menus.put(item.getName(),item);
406 log.fine("menus list contains " + this.menus.size() + " items");
407 }
408
409 /**
410 * Invoked by the Digester.
411 *
412 */
413 public void addMenuCategory(MenuCategory item) {
414 log.fine("adding menu category named \"" + item.getName() + "\"");
415 this.categories.put(item.getName(),item);
416 log.fine("categories list contains " + this.categories.size() + " items");
417 }
418
419 /**
420 * Invoked by the Digester.
421 *
422 */
423 public void addMenuItem(MenuItem item) {
424 log.fine("adding menu item named \"" + item.getName() + "\"");
425 this.menuItems.put(item.getName(),item);
426 log.fine("menuItems list contains " + this.menuItems.size() + " items");
427 }
428
429
430 }// end of class Sitemap