1    /*
2     * Copyright 2007 :torweg free software group
3     * 
4     * This program is free software: you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation, either version 3 of the License, or
7     * (at your option) any later version.
8     * 
9     * This program is distributed in the hope that it will be useful,
10    * but WITHOUT ANY WARRANTY; without even the implied warranty of
11    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    * GNU General Public License for more details.
13    * 
14    * You should have received a copy of the GNU General Public License
15    * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16    *
17    */
18   package org.torweg.pulse.component.core.site.map;
19   
20   import java.util.ArrayList;
21   import java.util.Collections;
22   import java.util.List;
23   
24   import org.hibernate.Session;
25   import org.hibernate.Transaction;
26   import org.hibernate.criterion.Restrictions;
27   import org.torweg.pulse.annotations.AnyAction;
28   import org.torweg.pulse.bundle.Controller;
29   import org.torweg.pulse.configuration.ConfigBean;
30   import org.torweg.pulse.configuration.DeprecatedConfigurable;
31   import org.torweg.pulse.invocation.lifecycle.Lifecycle;
32   import org.torweg.pulse.service.PulseException;
33   import org.torweg.pulse.service.request.ServiceRequest;
34   import org.torweg.pulse.site.map.Sitemap;
35   import org.torweg.pulse.site.map.SitemapNode;
36   import org.torweg.pulse.util.entity.Node;
37   
38   /**
39    * displays the visible part of the {@code Sitemap} for the
40    * {@code Locale} of the current {@code ServiceRequest}.
41    * <p>
42    * The configuration of the {@code SitemapMenuController} offers a choice
43    * of two different menu generation schemes:
44    * <ol type="a">
45    * <li>{@code tree}, for a tree like menu with the expanded to the current
46    * {@code SitemapNode}</li>
47    * <li>{@code compact}, for a three level display, showing the current
48    * {@code SitemapNode} and its neighboring nodes.</li>
49    * </ol>
50    * </p>
51    * <p>
52    * When the action of the {@code Command} equals
53    * {@code <em>sitemap</em>}, the complete visible {@code Sitemap} is
54    * returned.
55    * </p>
56    * 
57    * 
58    * @author Thomas Weber
59    * @version $Revision: 1914 $
60    */
61   public final class SitemapMenuController extends Controller implements
62           DeprecatedConfigurable {
63   
64       /**
65        * the configuration.
66        */
67       private SitemapMenuConfig config;
68   
69       /**
70        * @param c
71        *            the configuration
72        */
73       public void init(final ConfigBean c) {
74           this.config = (SitemapMenuConfig) c;
75       }
76   
77       /**
78        * displays the visible part of the {@code Sitemap} for the
79        * {@code Locale} of the current {@code ServiceRequest}.
80        * 
81        * @param request
82        *            the current {@code ServiceRequest}
83        * @return the {@code MenuResult}
84        */
85       @AnyAction
86       public MenuResult run(final ServiceRequest request) {
87           if (this.config.getMode().equals("threeLevelPlusCompact")) {
88               return buildThreeLevelPlus(request);
89           }
90           return buildTree(request);
91       }
92   
93       /**
94        * builds the tree menu for the current request.
95        * 
96        * @param request
97        *            the current request
98        * @return the tree menu
99        */
100      private MenuResult buildTree(final ServiceRequest request) {
101          Session s = Lifecycle.getHibernateDataSource().createNewSession();
102          Transaction tx = s.beginTransaction();
103          try {
104              SitemapNode requestedNode = getRequestedNode(request, s);
105  
106              // build bread crumb
107              List<SitemapNode> breadCrumb = buildBreadCrumb(requestedNode, true);
108  
109              tx.commit();
110  
111              return new MenuResult(breadCrumb.get(0), breadCrumb, request)
112                      .setRootNode(breadCrumb.get(0)).setThreeLevelBase(
113                              breadCrumb.get(0)).setRootSelected(
114                              (breadCrumb.size() == 1));
115          } catch (Exception e) {
116              tx.rollback();
117              throw new PulseException("Error: " + e.getLocalizedMessage(), e);
118          } finally {
119              s.close();
120          }
121      }
122  
123      /**
124       * creates a compact menu along with the first three levels from the
125       * sitemap's root.
126       * 
127       * @param request
128       *            the current request
129       * @return the menu result
130       */
131      private MenuResult buildThreeLevelPlus(final ServiceRequest request) {
132          Session s = Lifecycle.getHibernateDataSource().createNewSession();
133          Transaction tx = s.beginTransaction();
134          try {
135              SitemapNode requestedNode = getRequestedNode(request, s);
136  
137              // build bread crumb
138              List<SitemapNode> breadCrumb = buildBreadCrumb(requestedNode, false);
139  
140              // init first three levels
141              for (Node node : breadCrumb.get(0).getChildren()) {
142                  node.getChildren().size();
143              }
144  
145              SitemapNode baseNode;
146              if (breadCrumb.size() == 1) {
147                  // requested node is root node
148                  baseNode = requestedNode;
149              } else if (requestedNode.getChildren().size() > 0) {
150                  // requested node has children
151                  baseNode = (SitemapNode) requestedNode.getParent();
152                  baseNode.getChildren().size();
153              } else if (breadCrumb.size() > 2) {
154                  // requested node has no children, but more than one ancestor
155                  baseNode = (SitemapNode) requestedNode.getParent();
156                  baseNode.getChildren().size();
157                  baseNode = (SitemapNode) baseNode.getParent();
158                  baseNode.getChildren().size();
159              } else {
160                  baseNode = (SitemapNode) requestedNode.getParent();
161                  baseNode.getChildren().size();
162              }
163  
164              tx.commit();
165  
166              return new MenuResult(baseNode, breadCrumb, request).setRootNode(
167                      breadCrumb.get(0)).setThreeLevelBase(breadCrumb.get(0))
168                      .setRootSelected((breadCrumb.size() == 1));
169          } catch (Exception e) {
170              tx.rollback();
171              throw new PulseException("Error: " + e.getLocalizedMessage(), e);
172          } finally {
173              s.close();
174          }
175  
176      }
177  
178      /**
179       * returns the requested {@code SitemapNode} or the root node of the current
180       * locale, if either the sitemap ID of the current command is
181       * {@code null}, the requested node has a different {@code Locale} than
182       * the current request or the sitemap ID has no corresponding {@code
183       * SitemapNode}.
184       * 
185       * @param request
186       *            the current request
187       * @param s
188       *            the Hibernate<sup>TM</sup> session
189       * @return the requested sitemap node
190       */
191      private SitemapNode getRequestedNode(final ServiceRequest request,
192              final Session s) {
193          Long sitemapID = request.getCommand().getSitemapID();
194          SitemapNode requestedNode = null;
195          if (sitemapID != null) {
196              requestedNode = (SitemapNode) s.get(SitemapNode.class, sitemapID);
197              // check, if locales match
198              if ((requestedNode != null)
199                      && request.getLocale().equals(requestedNode.getLocale())) {
200                  return requestedNode;
201              }
202          }
203          return ((Sitemap) s.createCriteria(Sitemap.class).add(
204                  Restrictions.eq("locale", request.getLocale())).uniqueResult())
205                  .getRootNode();
206      }
207  
208      /**
209       * builds the bread-crumb.
210       * 
211       * @param selectedNode
212       *            the selected node
213       * @param initChildren
214       *            flag indicating whether to initialise the children of each
215       *            node in the bread crumb
216       * @return the bread crumb
217       */
218      private List<SitemapNode> buildBreadCrumb(final SitemapNode selectedNode,
219              final boolean initChildren) {
220          List<SitemapNode> bc = new ArrayList<SitemapNode>();
221          if (selectedNode != null) {
222              SitemapNode node = selectedNode;
223              do {
224                  if (initChildren) {
225                      node.getChildren().size();
226                  }
227                  bc.add(node);
228                  node = (SitemapNode) node.getParent();
229              } while (node != null);
230              Collections.reverse(bc);
231          }
232          return bc;
233      }
234  
235  }
236