/*
* Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com>
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: MenuTemplateTransformer.java 3953 2008-05-08 01:04:08Z gbevin $
*/
package com.uwyn.rife.crud.templates;
import com.uwyn.rife.config.RifeConfig;
import com.uwyn.rife.crud.CrudPropertyNames;
import com.uwyn.rife.crud.CrudSiteProcessor;
import com.uwyn.rife.crud.elements.admin.CrudElement;
import com.uwyn.rife.engine.ElementInfo;
import com.uwyn.rife.engine.FlowLink;
import com.uwyn.rife.engine.GlobalExit;
import com.uwyn.rife.engine.exceptions.EngineException;
import com.uwyn.rife.template.Template;
import com.uwyn.rife.template.exceptions.TemplateException;
import com.uwyn.rife.tools.Localization;
import com.uwyn.rife.tools.StringUtils;
import java.util.*;
public class MenuTemplateTransformer extends AdminTemplateTransformer
{
protected ElementInfo mEmbeddingElementInfo = null;
protected int mActiveMenuGroupId = 0;
protected String mActiveMenuItem = null;
protected String mTopElementId = null;
protected int mTopExitGroupId = Integer.MAX_VALUE;
public MenuTemplateTransformer(CrudElement element)
{
super(element);
mEmbeddingElementInfo = mElement.getEmbeddingElement().getElementInfo();
// auto-select the browse element when the arrival is active
if (mCrudPrefix != null &&
mEmbeddingElementInfo.getId().endsWith("."))
{
String exit_home = mCrudPrefix+CrudSiteProcessor.SUFFIX_EXIT_BROWSE;
if (mEmbeddingElementInfo.containsGlobalExit(exit_home))
{
mEmbeddingElementInfo = mEmbeddingElementInfo.getGlobalExitInfo(exit_home).getTarget();
}
}
}
public String getSupportedTemplateName()
{
return mElement.getPropertyString(CrudPropertyNames.TEMPLATE_NAME_MENU, buildGroupedTemplateName("menu"));
}
public String getTemplateNameDifferentiator()
{
FlowLink flow_link = mElement.getEmbeddingElement().getElementInfo().getFlowLink(mCrudPrefix+"-home");
if (null == flow_link)
{
return "";
}
String home_element_id = flow_link.getTarget().getId();
String id_prefix = home_element_id.substring(0, home_element_id.lastIndexOf("."));
String embedding_id = mElement.getEmbeddingElement().getElementInfo().getId();
if (!embedding_id.startsWith(id_prefix))
{
return "";
}
return "_"+embedding_id.substring(id_prefix.length()+1);
}
public void transformTemplate(Template t)
{
detectTopAndActiveMenuItem();
Map<Integer, List<String>> menu_levels = getMenuLevels();
List<String> highlighted_menu_items = getHighlightedMenuItems();
Map<ElementInfo, String> top_menu_items = getTopMenuItems();
renderTopMenuItems(t, top_menu_items, highlighted_menu_items);
renderMenuLevels(t, menu_levels, highlighted_menu_items);
}
protected void detectTopAndActiveMenuItem()
{
// determine the group ids of the top menu item and the active
// menu item, these will be used later to build the menu
String menu_item;
ElementInfo target;
int exit_groupid;
int target_groupid;
for (Map.Entry<String, GlobalExit> globalexit_entry : mEmbeddingElementInfo.getGlobalExitEntries())
{
menu_item = globalexit_entry.getKey();
// only consider the CMF admin global exits
if (menu_item.startsWith(CrudSiteProcessor.CRUD_PREFIX))
{
// get the information about this exit
target = globalexit_entry.getValue().getTarget();
exit_groupid = globalexit_entry.getValue().getGroupId();
target_groupid = target.getGroupId();
// adapt the top exit group id if the group id is smaller
// than the last one that was consider the top groupid
if (exit_groupid < mTopExitGroupId)
{
mTopExitGroupId = exit_groupid;
mTopElementId = target.getId();
}
// if the active menu groupid hasn't been set yet and
// the exit's target id corresponds to the element that
// is rendering the template, set the group id as the
// active menu group id
if (0 == mActiveMenuGroupId &&
target.getId().equals(mEmbeddingElementInfo.getId()))
{
mActiveMenuGroupId = target_groupid;
}
}
}
}
protected Map<Integer, List<String>> getMenuLevels()
{
Map<Integer, List<String>> primary_menu_levels = new TreeMap<Integer, List<String>>();
Map<Integer, List<String>> secondary_menu_levels = new TreeMap<Integer, List<String>>();
String menu_item;
ElementInfo target;
int exit_groupid;
int target_groupid;
// obtain each menu level and its items
int combined_groupid;
GlobalExit global_exit;
List<String> menu_level = null;
boolean is_secondary = false;
for (Map.Entry<String, GlobalExit> globalexit_entry : mEmbeddingElementInfo.getGlobalExitEntries())
{
is_secondary = false;
menu_item = globalexit_entry.getKey();
// only consider the CMF admin global exits
if (menu_item.startsWith(CrudSiteProcessor.CRUD_PREFIX) &&
!menu_item.endsWith(CrudSiteProcessor.SUFFIX_EXIT_SERVECONTENT))
{
// obtain the global exit
global_exit = globalexit_entry.getValue();
// get the target of this exit
target = global_exit.getTarget();
// get the group ids related to this exit
exit_groupid = global_exit.getGroupId();
target_groupid = target.getGroupId();
// handle the association home exits
if (target.containsProperty(CrudSiteProcessor.IDENTIFIER_ASSOCIATED_CLASSNAME) &&
target.getId().endsWith("."))
{
GlobalExit associated_edit_exit = mEmbeddingElementInfo.getGlobalExitInfo(CrudSiteProcessor.CRUD_PREFIX + target.getPropertyString(CrudSiteProcessor.IDENTIFIER_ASSOCIATED_CLASSNAME) + CrudSiteProcessor.SUFFIX_EXIT_EDIT);
if (associated_edit_exit != null)
{
exit_groupid = associated_edit_exit.getGroupId();
target_groupid = associated_edit_exit.getTarget().getGroupId();
is_secondary = true;
}
}
// combine the exit's group id and the top menu group id
// to get a unique combination for this menu creation
if (mTopElementId.equals(globalexit_entry.getValue().getTarget().getId()))
{
combined_groupid = mTopExitGroupId;
}
else
{
combined_groupid = exit_groupid + target_groupid;
}
// get the list of menu items, which is the menu level
// construct it if it doesn't exist yet
if (is_secondary)
{
menu_level = secondary_menu_levels.get(combined_groupid);
}
else
{
menu_level = primary_menu_levels.get(combined_groupid);
}
if (null == menu_level)
{
menu_level = new ArrayList<String>();
}
// strip away the crud-specific exit prefix
menu_item = menu_item.substring(CrudSiteProcessor.CRUD_PREFIX.length());
// if the target id of the exit corresponds to the element
// that is rendering the template, use the element's id
// as the active menu item
if (target.getId().equals(mEmbeddingElementInfo.getId()))
{
mActiveMenuItem = menu_item;
}
// add the exit to the menu level if it's not the top level
// and the target group id is lower that the active group id
// this ensures that no lower-level menus are rendered
if (!mTopElementId.equals(globalexit_entry.getValue().getTarget().getId()) &&
target_groupid <= mActiveMenuGroupId)
{
if (is_secondary)
{
secondary_menu_levels.put(combined_groupid, menu_level);
}
else
{
primary_menu_levels.put(combined_groupid, menu_level);
}
menu_level.add(menu_item);
}
}
}
// merge the primary and secondary menu levels
for (Map.Entry<Integer, List<String>> secondary_entry : secondary_menu_levels.entrySet())
{
if (primary_menu_levels.containsKey(secondary_entry.getKey()))
{
primary_menu_levels.get(secondary_entry.getKey()).addAll(secondary_entry.getValue());
}
else
{
primary_menu_levels.put(secondary_entry.getKey(), secondary_entry.getValue());
}
}
return primary_menu_levels;
}
protected List<String> getHighlightedMenuItems()
{
List<String> highlighted_menu_items = new ArrayList<String>();
String highlight_property = mEmbeddingElementInfo.getPropertyString(CrudSiteProcessor.IDENTIFIER_HIGHLIGHT);
// collect the highlighted menu items
String highlight_property_unprefixed = null;
FlowLink highlight_flowlink = null;
ElementInfo highlight_target = mEmbeddingElementInfo;
while (highlight_property != null)
{
highlight_property_unprefixed = highlight_property.substring(CrudSiteProcessor.CRUD_PREFIX.length());
// prevent circular references
if (highlighted_menu_items.contains(highlight_property_unprefixed))
{
break;
}
highlighted_menu_items.add(highlight_property_unprefixed);
// handle associations
if (highlight_property.endsWith(((Class)highlight_target.getProperty(CrudSiteProcessor.IDENTIFIER_CLASS)).getName()) &&
highlight_target.containsProperty(CrudSiteProcessor.IDENTIFIER_ASSOCIATED_CLASSNAME))
{
highlight_property = CrudSiteProcessor.CRUD_PREFIX+highlight_target.getProperty(CrudSiteProcessor.IDENTIFIER_ASSOCIATED_CLASSNAME)+CrudSiteProcessor.SUFFIX_EXIT_BROWSE;
}
else
{
// get the target element of the last highlight
highlight_flowlink = highlight_target.getFlowLink(highlight_property);
if (null == highlight_flowlink)
{
break;
}
highlight_target = highlight_flowlink.getTarget();
highlight_property = highlight_target.getPropertyString(CrudSiteProcessor.IDENTIFIER_HIGHLIGHT);
}
}
return highlighted_menu_items;
}
protected LinkedHashMap<ElementInfo, String> getTopMenuItems()
throws EngineException
{
LinkedHashMap<ElementInfo, String> top_menu_items = new LinkedHashMap<ElementInfo, String>();
String crud_site_prefix = null;
if (mTopElementId != null)
{
// top_element_id has the format ".SAMPLES.ACCOUNT."
// and we need ".SAMPLES.", so we need to go two dots backwards to
// construct crud_site_prefix
int first_dot_index = mTopElementId.lastIndexOf(".");
int second_dot_index = mTopElementId.lastIndexOf(".", first_dot_index - 1);
crud_site_prefix = mTopElementId.substring(0, second_dot_index + 1);
}
// if there's no top element id, this is template is printed from an element that sits outside
// the automatically generated crud site structure, it then assumes that it's part of the site
// that contains all the crud admin interfaces and uses that site id as the crud site prefix
else
{
String element_id = mElement.getElementInfo().getId();
int first_dot_index = element_id.lastIndexOf(".");
crud_site_prefix = element_id.substring(0, first_dot_index + 1);
}
// we go over all the element ids in the site and get the urls of the
// home templates these are then used to construct the top menu
int first_dot_index = -1;
for (String id : mEmbeddingElementInfo.getSite().getIds())
{
if (id.startsWith(crud_site_prefix))
{
first_dot_index = id.indexOf(".", crud_site_prefix.length());
if (-1 == first_dot_index)
{
continue;
}
// only work with the home element
String element_id = id.substring(first_dot_index);
if (!element_id.equals("."))
{
continue;
}
// obtain the element info that corresponds to the id
ElementInfo retrieved_element_info = mEmbeddingElementInfo.getSite().resolveId(id);
// get the class property and construct the home exit name
Object klass_property = retrieved_element_info.getProperty(CrudSiteProcessor.IDENTIFIER_CLASS);
if (null == klass_property)
{
continue;
}
String root_exit_name = ((Class)klass_property).getName();
top_menu_items.put(retrieved_element_info, root_exit_name);
}
}
return top_menu_items;
}
protected void renderTopMenuItems(Template template, Map<ElementInfo, String> topMenuItems, List<String> highlightedMenuItems)
throws TemplateException
{
List<ResourceBundle> bundles = new ArrayList<ResourceBundle>();
String language = RifeConfig.Tools.getDefaultLanguage();
if (mBeanClassNameEncoded != null)
{
bundles.add(Localization.getResourceBundle("l10n/crud/admin-" + mBeanClassNameEncoded, language));
}
bundles.add(Localization.getResourceBundle("l10n/crud/admin", language));
// prepend the top menu if this has been provided
for (ResourceBundle bundle : bundles)
{
if (null == bundle)
{
continue;
}
try
{
String prepend = bundle.getString("CRUD_ROOTMENU_PREPEND");
if (prepend.trim().length() > 0)
{
template.appendValue("menu_items", prepend);
break;
}
}
catch (MissingResourceException e)
{
continue;
}
}
for (Map.Entry<ElementInfo, String> top_menu_item : topMenuItems.entrySet())
{
// indicate when a menu item is active
if (mEmbeddingElementInfo.getId().equals(top_menu_item.getKey().getId()))
{
template.setBlock("menu_item_class", "menu_item_class-active");
}
else if (highlightedMenuItems.contains(top_menu_item.getValue()))
{
template.setBlock("menu_item_class", "menu_item_class-highlight");
}
// sets the menu item label
String url = top_menu_item.getKey().getUrl();
if (url.startsWith("/"))
{
url = url.substring(1);
}
template.setValue("url", url);
template.setValue("short-classname", getShortClassname(top_menu_item.getValue()));
template.setValue("exit_name", top_menu_item.getValue());
template.appendBlock("menu_items", "menu_item_top");
// remove the menu item decoration
template.removeValue("menu_item_class");
}
// append the top menu if this has been provided
for (ResourceBundle bundle : bundles)
{
if (null == bundle)
{
continue;
}
try
{
String append = bundle.getString("CRUD_ROOTMENU_APPEND");
if (append.trim().length() > 0)
{
template.appendValue("menu_items", append);
break;
}
}
catch (MissingResourceException e)
{
continue;
}
}
}
protected String getShortClassname(String menuItem)
{
String[] parts = StringUtils.splitToArray(menuItem, "-");
String classname = parts[0];
int last_dollarindex = classname.lastIndexOf("$");
if (last_dollarindex != -1)
{
return classname.substring(last_dollarindex+1);
}
int last_dotindex = classname.lastIndexOf(".");
if (last_dotindex != -1)
{
return classname.substring(last_dotindex+1);
}
return classname;
}
protected void renderMenuLevels(Template t, Map<Integer, List<String>> menuLevels, List<String> highlightedMenuItems)
throws TemplateException
{
// process each menu level seperately and each exit in each seperate
// level as a menu item
for (List<String> menu_level : menuLevels.values())
{
// display the menu level seperator after the first level
t.appendBlock("menu_items", "menu_seperator");
// process this level's menu items
for (String menu_item : menu_level)
{
// indicate when a menu item is active
if (mActiveMenuItem != null &&
mActiveMenuItem.equals(menu_item))
{
t.setBlock("menu_item_class", "menu_item_class-active");
}
else if (highlightedMenuItems.contains(menu_item))
{
t.setBlock("menu_item_class", "menu_item_class-highlight");
}
// sets the menu item label
t.setValue("exit_name", menu_item);
String[] parts = StringUtils.splitToArray(menu_item, "-");
if (parts.length <= 1)
{
t.setValue("short-exit_name", getShortClassname(menu_item));
}
else
{
t.setValue("short-exit_name", parts[1]);
}
t.appendBlock("menu_items", "menu_item");
// remove the menu item decoration
t.removeValue("menu_item_class");
}
}
}
}