KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > meshcms > core > SiteMap


1 /*
2  * MeshCMS - A simple CMS based on SiteMesh
3  * Copyright (C) 2004-2007 Luciano Vernaschi
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * You can contact the author at http://www.cromoteca.com
20  * and at info@cromoteca.com
21  */

22
23 package org.meshcms.core;
24
25 import java.io.*;
26 import java.util.*;
27 import org.meshcms.util.*;
28 import com.opensymphony.module.sitemesh.*;
29 import com.opensymphony.module.sitemesh.parser.*;
30
31 /**
32  * Contains the site map.
33  */

34 public class SiteMap extends DirectoryParser {
35   /**
36    * Name of the main file of a theme.
37    */

38   public static final String JavaDoc THEME_DECORATOR = "main.jsp";
39   
40   /**
41    * Name of the stylesheet of a theme.
42    */

43   public static final String JavaDoc THEME_CSS = "main.css";
44   
45   /**
46    * Name of the CSS that contains styles for elements of the MeshCMS interface
47    * (mail forms, editor and so on).
48    */

49   public static final String JavaDoc MESHCMS_CSS = "meshcms.css";
50   
51   public static final String JavaDoc MODULE_INCLUDE_FILE = "include.jsp";
52   
53   private WebSite webSite;
54   private SortedMap pagesMap;
55   private SiteMap oldSiteMap;
56   private long lastModified;
57   private Map currentWelcomes;
58   private List pagesList;
59   
60   private SortedMap themesMap;
61   private SortedMap modulesMap;
62   private List langList;
63   private Map pageCache;
64   
65   private Map redirCache;
66   private Path[] redirPaths;
67   
68   private boolean obsolete;
69   
70   /**
71    * Creates a new instance of SiteMap
72    */

73   public SiteMap(WebSite webSite) {
74     this.webSite = webSite;
75     setRecursive(true);
76     setSorted(true);
77     setProcessStartDir(true);
78     setInitialDir(webSite.getRootFile());
79     setDaemon(true);
80     setName("Site map parser for \"" + webSite.getTypeDescription() + '"');
81     pageCache = new HashMap();
82   }
83   
84   protected boolean preProcess() {
85     oldSiteMap = webSite.getSiteMap();
86     
87     if (oldSiteMap != null && oldSiteMap.isObsolete()) {
88       oldSiteMap = null;
89     }
90     
91     pagesMap = new TreeMap();
92     currentWelcomes = new TreeMap();
93     return true;
94   }
95   
96   protected boolean preProcessDirectory(File file, Path path) {
97     if (!webSite.isSystem(path)) {
98       Path wPath = webSite.findCurrentWelcome(path);
99       
100       if (wPath == null && path.isRoot()) {
101         String JavaDoc wName = webSite.getWelcomeFileNames()[0];
102         
103         try {
104           Utils.writeFully(new File(file, wName),
105               webSite.getHTMLTemplate("Home Page"));
106           webSite.getSiteInfo().setPageTheme(Path.ROOT, "default");
107           webSite.getSiteInfo().store();
108           wPath = new Path(wName);
109         } catch (IOException ex) {
110           webSite.log("Can't create home page for empty site", ex);
111         }
112       }
113       
114       if (wPath != null) {
115         currentWelcomes.put(path, wPath);
116         return true;
117       }
118     }
119     
120     return false;
121   }
122   
123   protected void processFile(File file, Path path) {
124     if (!FileTypes.isPage(path.getLastElement())) {
125       return;
126     }
127     
128     Path dirPath = path.getParent();
129     Path welcome = (Path) currentWelcomes.get(dirPath);
130     
131     if (welcome == null) {
132       return;
133     }
134     
135     Path parentPath = dirPath.getParent();
136     
137     if (!parentPath.isRelative() && currentWelcomes.get(parentPath) == null) {
138       return;
139     }
140     
141     path = welcome.equals(path) ? dirPath : path;
142     PageInfo pageInfo = null;
143     
144     if (oldSiteMap != null) {
145       pageInfo = oldSiteMap.getPageInfo(path);
146     }
147     
148     if (pageInfo == null) {
149       pageInfo = new PageInfo(webSite, path);
150     }
151     
152     if (pageInfo.getLastModified() != file.lastModified()) {
153       HTMLPageParser fpp = new HTMLPageParser();
154       Reader reader = null;
155       
156       try {
157         reader = new BufferedReader(new FileReader(file));
158         Page page = fpp.parse(Utils.readAllChars(reader));
159         reader.close();
160         String JavaDoc title = page.getTitle();
161         
162         if (Utils.isNullOrWhitespace(title)) {
163           title = Utils.beautify(Utils.removeExtension(path), true);
164         }
165         
166         pageInfo.setTitle(title);
167         pageInfo.setLastModified(file.lastModified());
168         
169         /*
170         String[] pKeys = page.getPropertyKeys();
171         String pageCharset = null;
172          
173         for (int i = 0; i < pKeys.length; i++) {
174           if (pKeys[i].toLowerCase().indexOf("content-type") != -1) {
175             pageCharset = WebUtils.parseCharset(page.getProperty(pKeys[i]));
176           }
177         }
178          */

179       } catch (Exception JavaDoc ex) {
180         pageInfo.setTitle(Utils.beautify(path.getLastElement(), true));
181         pageInfo.setLastModified(0L);
182       } finally {
183         if (reader != null) {
184           try {
185             reader.close();
186           } catch (IOException ex) {
187             webSite.log("Can't close file " + file, ex);
188           }
189         }
190       }
191     }
192     
193     pagesMap.put(path, pageInfo);
194   }
195   
196   protected void postProcess() {
197     pagesMap = Collections.unmodifiableSortedMap(pagesMap);
198     oldSiteMap = null;
199     
200     pagesList = new ArrayList(pagesMap.values());
201     Collections.sort(pagesList, new PageInfoComparator(this, webSite.getSiteInfo()));
202     pagesList = Collections.unmodifiableList(pagesList);
203     
204     langList = new ArrayList();
205     Iterator iter = getPagesInDirectory(Path.ROOT, false).iterator();
206     
207     while (iter.hasNext()) {
208       Path path = ((PageInfo) iter.next()).getPath();
209       
210       if (path.getElementCount() == 1) {
211         Locale locale = Utils.getLocale(path.getElementAt(0));
212         
213         if (locale != null) {
214           langList.add(new CodeLocalePair(path.getElementAt(0), locale));
215         }
216       }
217     }
218     
219     langList = Collections.unmodifiableList(langList);
220     setLastModified();
221     webSite.setSiteMap(this);
222     // webSite.getSiteInfo().cleanupSiteInfo(); // deprecated
223
}
224   
225   /**
226    * @return the <code>PageInfo</code> for the given path.
227    */

228   public PageInfo getPageInfo(Path path) {
229     return (PageInfo) pagesMap.get(getPathInMenu(path));
230   }
231   
232   /**
233    * @return the <code>PageInfo</code> for parent of the page at the given path.
234    */

235   public PageInfo getParentPageInfo(Path path) {
236     path = getPathInMenu(path).getParent();
237     return path.isRelative() ? null : (PageInfo) pagesMap.get(path);
238   }
239   
240   /**
241    * @return the given path unless it is the current welcome file in its
242    * folder; in this case the folder path is returned.
243    */

244   public Path getPathInMenu(Path path) {
245     return currentWelcomes.containsValue(path) ? path.getParent() : path;
246   }
247   
248   /**
249    * @return the given path unless it is a folder with a welcome file; in this
250    * case the welcome file path is returned.
251    */

252   public Path getServedPath(Path path) {
253     Path welcome = (Path) currentWelcomes.get(path);
254     return (welcome == null) ? path : welcome;
255   }
256   
257   /**
258    * @return the path of the welcome file for the given directory path. This
259    * method returns null if the path is not a directory or if there is no
260    * welcome file into it.
261    */

262   public Path getCurrentWelcome(Path dirPath) {
263     return (Path) currentWelcomes.get(dirPath);
264   }
265   
266   /**
267    * Checks if the given path is the welcome file for its directory.
268    */

269   public boolean isCurrentWelcome(Path path) {
270     return currentWelcomes.containsValue(path);
271   }
272   
273   /**
274    * Sets the last modification time to the current time.
275    */

276   void setLastModified() {
277     lastModified = System.currentTimeMillis();
278   }
279   
280   /**
281    * @return the last modification time.
282    */

283   public long getLastModified() {
284     return lastModified;
285   }
286   
287   /**
288    * Returns the code needed to create a menu or a tree with the scripts
289    * created by <a HREF="http://www.softcomplex.com/">SoftComplex</a>.
290    *
291    * @param contextPath the context path as returned from
292    * <code>HttpServletRequest.getContextPath()</code>
293    * @param path the root path for the menu (if null, the root path is used)
294    * @param tree true to get the items for a tree, false to get
295    * the items for a menu
296    *
297    * @return a string that can be used as content for the menu_items.js file
298    * needed by those scripts
299    */

300   public String JavaDoc getTigraItems(String JavaDoc contextPath, Path path, boolean tree) {
301     return getTigraItems(contextPath, path, tree, false);
302   }
303   
304   /**
305    * Returns the code needed to create a menu or a tree with the scripts
306    * created by <a HREF="http://www.softcomplex.com/">SoftComplex</a>.
307    *
308    * @param contextPath the context path as returned from
309    * <code>HttpServletRequest.getContextPath()</code>
310    * @param path the root path for the menu (if null, the root path is used)
311    * @param tree true to get the items for a tree, false to get
312    * the items for a menu
313    * @param allowHiding honours the "hide submenu" option
314    *
315    * @return a string that can be used as content for the menu_items.js file
316    * needed by those scripts
317    */

318   public String JavaDoc getTigraItems(String JavaDoc contextPath, Path path, boolean tree, boolean allowHiding) {
319     if (path == null) {
320       path = Path.ROOT;
321     }
322     
323     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
324     
325     sb.append("var ").append(tree ? "TREE" : "MENU").append("_ITEMS = [");
326     
327     int baseLevel = path.getElementCount() + 1;
328     SiteInfo siteInfo = webSite.getSiteInfo();
329     SiteMapIterator iter = new SiteMapIterator(webSite, path);
330     iter.setSkipHiddenSubPages(allowHiding);
331     PageInfo current;
332     PageInfo previous = null;
333     int level;
334     
335     do {
336       if (iter.hasNext()) {
337         current = (PageInfo) iter.next();
338         level = Math.max(current.getLevel(), baseLevel);
339       } else {
340         current = null;
341         level = baseLevel;
342       }
343       
344       if (previous != null) {
345         int previousLevel = Math.max(previous.getLevel(), baseLevel);
346         
347         for (int j = level; j > previousLevel; j--) {
348           // nothing here
349
}
350         
351         if (level <= previousLevel) {
352           sb.append(", null]");
353         }
354         
355         for (int j = previousLevel - 1; j >= level; j--) {
356           sb.append("\n");
357           
358           for (int k = baseLevel; k <= j; k++) {
359             sb.append(" ");
360           }
361           
362           sb.append("]");
363         }
364         
365       }
366       
367       if (current != null) {
368         if (previous != null) {
369           sb.append(",");
370         }
371         
372         sb.append("\n");
373         
374         for (int j = baseLevel; j <= level; j++) {
375           sb.append(" ");
376         }
377         
378         sb.append("['");
379         sb.append(Utils.escapeSingleQuotes(siteInfo.getPageTitle(current)));
380         sb.append("', ");
381         String JavaDoc link = webSite.getLink(current);
382         sb.append(link == null ? "null" : "'" + contextPath + link + "'");
383         
384         if (!tree) {
385           sb.append(", null");
386         }
387         
388         previous = current;
389       }
390     } while (current != null);
391     
392     sb.append("\n];");
393     return sb.toString();
394   }
395   
396   /**
397    * @return the pages contained in the menu as a SortedMap, using the given
398    * path as root. All keys are of type <code>Path</code> and all values are of
399    * type <code>PageInfo</code>. Note that the ordering of the map is the
400    * natural order of <code>Path</code>.
401    *
402    * @see org.meshcms.util.Path
403    */

404   public SortedMap getPagesMap(Path root) {
405     if (root == null) {
406       root = Path.ROOT;
407     }
408     
409     return root.isRoot() ? pagesMap : pagesMap.subMap(root, root.successor());
410   }
411   
412   public List getLangList() {
413     return langList;
414   }
415   
416   /**
417    * @return true if there is at least one page whose parent path is the
418    * given one.
419    */

420   public boolean hasChildrenPages(Path path) {
421     if (!webSite.isDirectory(path)) {
422       return false;
423     }
424     
425     int n = path.getElementCount() + 1;
426     SortedMap map = getPagesMap(path);
427     
428     if (map != null) {
429       Iterator iter = map.keySet().iterator();
430       
431       while (iter.hasNext()) {
432         Path p = (Path) iter.next();
433         
434         if (p.getElementCount() == n) {
435           return true;
436         }
437       }
438     }
439     
440     return false;
441   }
442   
443   /**
444    * @return a list of pages contained in the directory that contains the given
445    * path; if the path denotes a directory, its contents are returned.
446    *
447    * @param includeDir if true, the directory itself is included in the list
448    */

449   public List getPagesInDirectory(Path path, boolean includeDir) {
450     PageInfo rootPage = getPageInfo(webSite.getDirectory(path));
451     int idx = pagesList.indexOf(rootPage);
452     
453     if (idx < 0) {
454       return null;
455     }
456     
457     List list = new ArrayList();
458     
459     if (includeDir) {
460       list.add(rootPage);
461     }
462     
463     for (int i = idx + 1; i < pagesList.size(); i++) {
464       PageInfo pi = (PageInfo) pagesList.get(i);
465       int n = pi.getLevel() - rootPage.getLevel();
466       
467       if (n <= 0) {
468         break;
469       } else if (n == 1) {
470         list.add(pi);
471       }
472     }
473     
474     return list;
475   }
476   
477   /**
478    * @return the pages contained in the menu as a unmodifiable List.
479    * All members of the list are of type <code>PageInfo</code>.
480    * Pages are sorted using a {@link PageInfoComparator}.
481    */

482   public List getPagesList() {
483     return pagesList;
484   }
485   
486   /**
487    * @return the pages contained in the menu as a unmodifiable List, using the given path as
488    * root path. All members of the list are of type <code>PageInfo</code>.
489    * Pages are sorted using a {@link PageInfoComparator}.
490    */

491   public List getPagesList(Path root) {
492     root = webSite.getDirectory(root);
493     
494     if (root.isRoot()) {
495       return pagesList;
496     }
497     
498     PageInfo rootPage = getPageInfo(root);
499     int idx = pagesList.indexOf(rootPage);
500     
501     if (idx < 0) {
502       return null;
503     }
504     
505     int rootLevel = rootPage.getLevel();
506     
507     for (int i = idx + 1; i < pagesList.size(); i++) {
508       if (((PageInfo) pagesList.get(i)).getLevel() <= rootLevel) {
509         return pagesList.subList(idx, i);
510       }
511     }
512     
513     return pagesList.subList(idx, pagesList.size());
514   }
515   
516   /**
517    * Returns the pages contained in the menu as a unmodifiable List, using the given path as
518    * root path. All members of the list are of type <code>PageInfo</code>.
519    * Pages are sorted using a {@link PageInfoComparator}.
520    * NB: This method excludes hidden submenus.
521    *
522    * @deprecated use a {@link org.meshcms.core.SiteMapIterator} and set
523    * {@link org.meshcms.core.SiteMapIterator#setSkipHiddenSubPages} to
524    * <code>true</code> instead.
525    */

526   public List getPagesListNoHiddenSubmenus(Path root) {
527     SiteInfo siteInfo = webSite.getSiteInfo();
528     List pagesListHiddenSubmenus = new ArrayList(this.getPagesList(root));
529     Path currentHiddenSubmenuPath = null;
530     int i = 0;
531     while (i < pagesListHiddenSubmenus.size()) {
532       Path currentPath = ((PageInfo)pagesListHiddenSubmenus.get(i)).getPath();
533       if (currentHiddenSubmenuPath != null) {
534         if (currentPath.isContainedIn(currentHiddenSubmenuPath)) {
535           pagesListHiddenSubmenus.remove(i);
536         } else {
537           currentHiddenSubmenuPath = null;
538         }
539       }
540       if (currentHiddenSubmenuPath == null && siteInfo.getHideSubmenu(currentPath)) {
541         currentHiddenSubmenuPath = currentPath;
542       }
543       i++;
544     }
545     return Collections.unmodifiableList(pagesListHiddenSubmenus);
546   }
547   
548   /**
549    * @return the breadcrumbs from the root path (included) to the given path
550    * (<em>not</em> included).
551    */

552   public PageInfo[] getBreadcrumbs(Path path) {
553     path = getPathInMenu(path);
554     List list = new ArrayList();
555     
556     for (int i = 0; i < path.getElementCount(); i++) {
557       Path partial = path.getPartial(i);
558       PageInfo pi = getPageInfo(partial);
559       
560       if (pi != null) {
561         list.add(pi);
562       } else if (partial.equals(webSite.getAdminPath())) {
563         PageInfo api = new PageInfo(webSite, webSite.getAdminPath());
564         api.setTitle("MeshCMS");
565         list.add(api);
566       }
567     }
568     
569     if (list.size() != 0) {
570       return (PageInfo[]) list.toArray(new PageInfo[list.size()]);
571     }
572     
573     return null;
574   }
575   
576   /**
577    * @return an array of the names of all available themes.
578    */

579   public String JavaDoc[] getThemeNames() {
580     Set keys = getThemesMap().keySet();
581     return (String JavaDoc[]) keys.toArray(new String JavaDoc[keys.size()]);
582   }
583   
584   /**
585    * @return an array of the names of all available modules.
586    */

587   public String JavaDoc[] getModuleNames() {
588     Set keys = getModulesMap().keySet();
589     return (String JavaDoc[]) keys.toArray(new String JavaDoc[keys.size()]);
590   }
591   
592   /**
593    * Caches a page.
594    */

595   public void cache(Path path, byte[] b) {
596     pageCache.put(path, b);
597   }
598   
599   /**
600    * Removes a page from the cache.
601    */

602   public void removeFromCache(Path path) {
603     pageCache.remove(path);
604   }
605   
606   /**
607    * Gets a page from the cache.
608    */

609   public byte[] getCached(Path path) {
610     return (byte[]) pageCache.get(path);
611   }
612   
613   /**
614    * Check if a page is available in the cache.
615    */

616   public boolean isCached(Path path) {
617     return pageCache.containsKey(path);
618   }
619   
620   public SortedMap getThemesMap() {
621     if (themesMap == null) {
622       themesMap = new TreeMap();
623       addDirItemsToMap(themesMap, webSite.getAdminThemesPath(), THEME_DECORATOR);
624       addDirItemsToMap(themesMap, webSite.getCustomThemesPath(), THEME_DECORATOR);
625       themesMap = Collections.unmodifiableSortedMap(themesMap);
626     }
627     
628     return themesMap;
629   }
630   
631   public SortedMap getModulesMap() {
632     if (modulesMap == null) {
633       modulesMap = new TreeMap();
634       addDirItemsToMap(modulesMap, webSite.getAdminModulesPath(),
635           MODULE_INCLUDE_FILE);
636       addDirItemsToMap(modulesMap, webSite.getCustomModulesPath(),
637           MODULE_INCLUDE_FILE);
638       modulesMap = Collections.unmodifiableSortedMap(modulesMap);
639     }
640     
641     return modulesMap;
642   }
643   
644   private void addDirItemsToMap(Map map, Path path, String JavaDoc insideDir) {
645     File dir = webSite.getFile(path);
646     
647     if (dir.isDirectory()) {
648       String JavaDoc[] files = dir.list();
649       
650       if (files != null) {
651         for (int i = 0; i < files.length; i++) {
652           Path subPath = path.add(files[i]);
653           
654           if (insideDir == null || webSite.getFile(subPath.add(insideDir)).exists()) {
655             map.put(files[i], subPath);
656           }
657         }
658       }
659     }
660   }
661   
662   /**
663    * @see #setObsolete(boolean)
664    */

665   public boolean isObsolete() {
666     return obsolete;
667   }
668   
669   /**
670    * When obsolete, info contained in this site map will be discarded when a
671    * new site map is created.
672    */

673   public void setObsolete(boolean obsolete) {
674     this.obsolete = obsolete;
675   }
676   
677   public Path getRedirMatch(Path requestedPath) {
678     if (redirCache == null) {
679       redirCache = new HashMap();
680       List list = getPagesList();
681       redirPaths = new Path[list.size()];
682       Iterator iter = list.iterator();
683       
684       for (int i = 0; iter.hasNext(); i++) {
685         redirPaths[i] = ((PageInfo) iter.next()).getPath();
686       }
687     }
688     
689     Path result = null;
690     int best = 0;
691     
692     if (Utils.searchString(WebUtils.DEFAULT_WELCOME_FILES,
693         requestedPath.getLastElement(), false) >= 0) {
694       requestedPath = requestedPath.getPartial(requestedPath.getElementCount() - 1);
695     }
696     
697     if (redirCache.containsKey(requestedPath)) {
698       result = (Path) redirCache.get(requestedPath);
699     } else {
700       for (int i = 0; i < redirPaths.length; i++) {
701         String JavaDoc[] commonPart = Utils.commonPart(requestedPath.getElements(),
702             redirPaths[i].getElements(), true);
703         
704         if (commonPart != null && commonPart.length > best) {
705           result = redirPaths[i];
706           best = commonPart.length;
707         }
708       }
709       
710       redirCache.put(requestedPath, result);
711     }
712     
713     return result;
714   }
715   
716   public static class CodeLocalePair {
717     private String JavaDoc code;
718     private Locale locale;
719     private String JavaDoc name;
720     
721     public CodeLocalePair(String JavaDoc code, Locale locale) {
722       this.code = code;
723       this.locale = locale;
724       name = Utils.toTitleCase(locale.getDisplayName(locale));
725     }
726     
727     public String JavaDoc getCode() {
728       return code;
729     }
730     
731     public Locale getLocale() {
732       return locale;
733     }
734     
735     public String JavaDoc getName() {
736       return name;
737     }
738   }
739 }
740
Popular Tags