/**
* Copyright 2015-2016 Stephan Rauh, http://www.beyondjava.net,
* and Riccardo Massera (TheCoder4.Eu)
* This file is part of BootsFaces.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.bootsfaces.listeners;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.faces.FacesException;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIViewRoot;
import javax.faces.component.html.HtmlBody;
import javax.faces.component.html.HtmlHead;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;
import net.bootsfaces.C;
import net.bootsfaces.beans.ELTools;
import net.bootsfaces.utils.BsfUtils;
/**
* This class adds the resource needed by BootsFaces and ensures that they are
* loaded in the correct order. It replaces the former HeadListener.
*
* @author Stephan Rauh
*/
public class AddResourcesListener implements SystemEventListener {
private static final Logger LOGGER = Logger.getLogger("net.bootsfaces.listeners.AddResourcesListener");
/**
* List of JS files that have to be loaded earlier than most JS files.
*/
private static final String BASIC_JS_RESOURCE_KEY = "net.bootsfaces.listeners.AddResourcesListener.BasicJSResourceFiles";
/**
* Components can request JS resources by registering them in the ViewMap,
* using the RESOURCE_KEY.
*/
private static final String RESOURCE_KEY = "net.bootsfaces.listeners.AddResourcesListener.ResourceFiles";
/**
* Components can request themed CSS resources by registering them in the ViewMap,
* using the THEME_RESOURCE_KEY.
*/
private static final String THEME_RESOURCE_KEY = "net.bootsfaces.listeners.AddResourcesListener.ThemedResourceFiles";
/**
* Components can request extended CSS resources by registering them in the ViewMap,
* using the EXT_RESOURCE_KEY.
*/
private static final String EXT_RESOURCE_KEY = "net.bootsfaces.listeners.AddResourcesListener.ExtResourceFiles";
private static final String SCRIPT_RENDERER = "javax.faces.resource.Script",
CSS_RENDERER = "javax.faces.resource.Stylesheet";
static {
LOGGER.info("This application is running on BootsFaces"+C.BSFVERSION+"-"+C.BSFRELEASE_STATUS);
//LOGGER.info("This application is running on BootsFaces 1.0.1-SNAPSHOT");
}
/**
* Returns true if the source is an instance of {@link UIViewRoot}.
*/
@Override
public boolean isListenerForSource(Object source) {
return (source instanceof UIViewRoot);
}
/**
* Trigger adding the resources if and only if the event has been fired by
* UIViewRoot.
*/
public void processEvent(SystemEvent event) throws AbortProcessingException {
FacesContext context = FacesContext.getCurrentInstance();
UIViewRoot root = context.getViewRoot();
// render the resources only if there is at least one bsf component
if(ensureExistBootsfacesComponent(root, context)) {
addCSS(root, context);
addJavascript(root, context);
addMetaTags(root, context);
}
}
/**
* Check if there is almost one bootsfaces component in page.
* If yes, load the correct items.
*
* To ensure that, it checks first the viewMap to find specific bootsfaces key.
* If it found nothing, it check for components that has as a resource lib, the "bsf" lib.
*
* @param root <- the UIViewRoot
* @param context <- the faces context
* @return
*/
private boolean ensureExistBootsfacesComponent(UIViewRoot root, FacesContext context) {
Map<String, Object> viewMap = root.getViewMap();
// check explicit js request
if(viewMap.get(RESOURCE_KEY) != null) return true;
// check explicit css request
if(viewMap.get(THEME_RESOURCE_KEY) != null) return true;
// check explicit css request
if(viewMap.get(EXT_RESOURCE_KEY) != null) return true;
// check referenced bsf request
if(findBsfComponent(root, C.BSF_LIBRARY) != null) return true;
return false;
}
/**
* Check all components in page to find one that
* has as resource library the target library.
* I use this method to check existence of a BsF component
* because, at this level, the getComponentResource returns always null
*
* @param parent the parent component
* @param targetLib the lib to search
* @return
*/
public static UIComponent findBsfComponent(UIComponent parent, String targetLib) {
if (targetLib.equalsIgnoreCase((String)parent.getAttributes().get("library"))) {
return parent;
}
Iterator<UIComponent> kids = parent.getFacetsAndChildren();
while (kids.hasNext()) {
UIComponent found = findBsfComponent(kids.next(), targetLib);
if (found != null) {
return found;
}
}
return null;
}
/**
* Add the viewport meta tag if not disabled from context-param
*
* @param root
* @param context
* @param isProduction
*/
private void addMetaTags(UIViewRoot root, FacesContext context) {
// Check context-param
String viewportParam = BsfUtils.getInitParam(C.P_VIEWPORT, context);
viewportParam = evalELIfPossible(viewportParam);
String content = "width=device-width, initial-scale=1";
if (!viewportParam.isEmpty() && isFalseOrNo(viewportParam))
return;
if(!viewportParam.isEmpty() && !isTrueOrYes(viewportParam))
content = viewportParam;
// Otherwise
String viewportMeta = "<meta name=\"viewport\" content=\"" + content + "\"/>";
UIOutput viewport = new UIOutput();
viewport.setRendererType("javax.faces.Text");
viewport.getAttributes().put("escape", false);
viewport.setValue(viewportMeta);
UIComponent header = findHeader(root);
if(header != null) {
header.getChildren().add(0, viewport);
}
}
/**
* Add the required CSS files and the FontAwesome CDN link.
*
* @param root
* The UIViewRoot of the JSF tree.
* @param context
* The current FacesContext
*/
private void addCSS(UIViewRoot root, FacesContext context) {
// The following code is needed to diagnose the warning "Unable to save dynamic action with clientId 'j_id...'"
// List<UIComponent> r = root.getComponentResources(context, "head");
// System.out.println("**************");
// for (UIComponent ava : r) {
// String name = (String) ava.getAttributes().get("name");
// System.out.println(ava.getClientId(context) +":" + name + " " + ava.getClass().getSimpleName());
// }
// System.out.println("**************");
// end of the diagnostic code
//1) First load Theme files (core.css + theme.css)
String theme = loadTheme(root, context);
//deactivate FontAwesome support if the no-fa facet is found in the h:head tag
UIComponent header = findHeader(root);
boolean useCDNImportForFontAwesome = (null == header) || (null == header.getFacet("no-fa"));
if (useCDNImportForFontAwesome) {
String useCDN = BsfUtils.getInitParam("net.bootsfaces.get_fontawesome_from_cdn");
if (null != useCDN) {
useCDN = ELTools.evalAsString(useCDN);
}
if (null != useCDN) {
useCDNImportForFontAwesome = !isFalseOrNo(useCDN);
}
}
// Do we have to add font-awesome, or are the resources already there?
List<UIComponent> availableResources = root.getComponentResources(context, "head");
for (UIComponent ava : availableResources) {
String name = (String) ava.getAttributes().get("name");
if (null != name) {
name = name.toLowerCase();
if ((name.contains("font-awesome") || name.contains("fontawesome")) && name.endsWith("css"))
useCDNImportForFontAwesome = false;
}
}
//2) Font Awesome
if (useCDNImportForFontAwesome) {
InternalFALink output = new InternalFALink();
output.getAttributes().put("src", C.FONTAWESOME_CDN_URL);
addResourceIfNecessary(root, context, output);
}
//3) Bootstrap from CDN (TODO: check removeBootstrapResources)
boolean loadBootstrapFromCDN = shouldLibraryBeLoaded("net.bootsfaces.get_bootstrap_from_cdn", false);
if (loadBootstrapFromCDN) { removeBootstrapResources(root, context); }
/*
List<UIComponent> res = root.getComponentResources(context, "head");
for (UIComponent ava : res) {
String library = (String) ava.getAttributes().get("library");
if (library != null && library.equals("bsf")) {
String name = (String) ava.getAttributes().get("name");
if (null != name && name.endsWith(".css") && name.startsWith("css/")) {
ava.getAttributes().remove("name");
int pos = name.lastIndexOf('/');
ava.getAttributes().put("name", "css/" + theme + "/" + name.substring(pos + 1));
}
}
} */
@SuppressWarnings("unchecked")
List<String> extCSSMap = (List<String>) root.getViewMap().get(EXT_RESOURCE_KEY);
if(extCSSMap != null) {
for (String file: extCSSMap) {
String name = "css/" + file;
createAndAddComponent(root, context, CSS_RENDERER, name, C.BSF_LIBRARY);
}
}
@SuppressWarnings("unchecked")
List<String> themedCSSMap = (List<String>) root.getViewMap().get(THEME_RESOURCE_KEY);
if(themedCSSMap != null) {
for (String file: themedCSSMap) {
String name = "css/" + theme + "/" + file;
// Glyphicons icons now are in core.css if(file.equals("icons.css")) //the icons.css file isn't found in a theme folder
// Glyphicons icons now are in core.css name = "css/icons.css"; //look for it under the css root instead
createAndAddComponent(root, context, CSS_RENDERER, name, C.BSF_LIBRARY);
}
}
// Glyphicons icons now are in core.css
//String name = "css/icons.css";
//createAndAddComponent(root, context, CSS_RENDERER, name, C.BSF_LIBRARY);
if (theme.equals("patternfly")) {
createAndAddComponent(root, context, CSS_RENDERER, "css/patternfly/bootstrap-switch.css", C.BSF_LIBRARY);
// remove datatables.min.css
/*
for (UIComponent resource : root.getComponentResources(context, "head")) {
String library = (String) resource.getAttributes().get("library");
if (C.BSF_LIBRARY.equals(library)) {
String name = (String) resource.getAttributes().get("name");
if ("css/datatables.min.css".equals(name)) {
resource.setInView(false);
root.removeComponentResource(context, resource);
}
}
}
*/
}
//Add mandatory CSS bsf.css
createAndAddComponent(root, context, CSS_RENDERER, "css/bsf.css", C.BSF_LIBRARY);
}
private String loadTheme(UIViewRoot root, FacesContext context) {
/*
* As of v0.8.0 we have two Context Parameters: BootsFaces_USETHEME - as
* in previous versions controls if the current theme is to be rendered
* in the Flat variant (default) or in its Enhanced variant, with
* shadows and decorations turned on. BootsFaces_THEME - controls the
* Theme to use: the value "default" is plain Bootstrap, the other
* options are a Bootswach Theme name (lowercase) or "custom". If custom
* is chosen, you will have to provide your custom CSS in the "other"
* folder.
*/
ResourceHandler rh = context.getApplication().getResourceHandler();
String theme = getTheme(context);
// Theme loading
if (isFontAwesomeComponentUsedAndRemoveIt() || (!theme.equalsIgnoreCase("other"))) {
//String filename = "bsf.css";
String filename = "core.css";
Resource themeResource = rh.createResource("css/" + theme + "/" + filename, C.BSF_LIBRARY);
if (themeResource == null) {
throw new FacesException("Error loading theme, cannot find \"" + "css/" + theme + "/" + filename + "\" resource of \""
+ C.BSF_LIBRARY + "\" library");
} else {
String name = "css/" + theme + "/" + filename;
createAndAddComponent(root, context, CSS_RENDERER, name, C.BSF_LIBRARY);
}
}
// If the BootsFaces_USETHEME parameter is true, render Theme CSS link
String usetheme = evalELIfPossible(BsfUtils.getInitParam(C.P_USETHEME, context));
if (!usetheme.isEmpty() && isTrueOrYes(usetheme)) {
String name = "css/" + theme + "/theme.css";
createAndAddComponent(root, context, CSS_RENDERER, name, C.BSF_LIBRARY);
}
return theme;
}
private String getTheme(FacesContext context) {
String theme = evalELIfPossible(BsfUtils.getInitParam(C.P_THEME, context));
if (!theme.isEmpty()) {
if (theme.equalsIgnoreCase("custom")) {
theme = "other";
}
} else theme = "default";
return theme;
}
/**
* Add the required Javascript files and the FontAwesome CDN link.
*
* @param root
* The UIViewRoot of the JSF tree.
* @param context
* The current FacesContext
*/
private void addJavascript(UIViewRoot root, FacesContext context) {
// The following code is needed to diagnose the warning "Unable to save dynamic action with clientId 'j_id...'"
// List<UIComponent> r = root.getComponentResources(context, "head");
// System.out.println("**************");
// for (UIComponent ava : r) {
// String name = (String) ava.getAttributes().get("name");
// System.out.println(ava.getClientId(context) +":" + name + " " + ava.getClass().getSimpleName());
// }
// System.out.println("**************");
// end of the diagnostic code
boolean loadJQuery = shouldLibraryBeLoaded("net.bootsfaces.get_jquery_from_cdn", true);
boolean loadJQueryUI = shouldLibraryBeLoaded("net.bootsfaces.get_jqueryui_from_cdn", true);
// Do we have to add jQuery, or are the resources already there?
List<UIComponent> availableResources = root.getComponentResources(context, "head");
for (UIComponent ava : availableResources) {
String name = (String) ava.getAttributes().get("name");
if (null != name) {
name = name.toLowerCase();
if (name.startsWith("jquery-ui") && name.endsWith(".js"))
loadJQueryUI = false;
else if (name.startsWith("jquery") && name.endsWith(".js"))
loadJQuery = false;
}
}
addMandatoryLibraries(root, context, loadJQuery, loadJQueryUI);
String blockUI = BsfUtils.getInitParam("net.bootsfaces.blockUI", context);
if (null != blockUI)
blockUI = ELTools.evalAsString(blockUI);
if (null != blockUI && isTrueOrYes(blockUI)) {
createAndAddComponent(root, context, SCRIPT_RENDERER, "js/jquery.blockUI.js", C.BSF_LIBRARY);
}
removeDuplicateResources(root, context);
addResourceIfNecessary(root, context, new InternalIE8CompatiblityLinks());
enforceCorrectLoadOrder(root, context);
}
private Map<String, Object> addMandatoryLibraries(
UIViewRoot root, FacesContext context, boolean loadJQuery, boolean loadJQueryUI) {
/* We used to check if a single component needs the bsf.js, jsf or jquery library.
* This can be an error prone approach so we add all of them (if not different specified)
*/
createAndAddComponent(root, context, SCRIPT_RENDERER, "jsf.js", "javax.faces");
createAndAddComponent(root, context, SCRIPT_RENDERER, "js/bsf.js", C.BSF_LIBRARY);
if (loadJQuery)
createAndAddComponent(root, context, SCRIPT_RENDERER, "jq/jquery.js", C.BSF_LIBRARY);
Map<String, Object> viewMap = root.getViewMap();
@SuppressWarnings("unchecked")
Map<String, String> basicResourceMap = (Map<String, String>) viewMap.get(BASIC_JS_RESOURCE_KEY);
if (null != basicResourceMap) {
for (Entry<String, String> entry : basicResourceMap.entrySet()) {
String file = entry.getValue();
String library = entry.getKey().substring(0, entry.getKey().length() - file.length() - 1);
if (!"jq/jquery.js".equals(file) || !C.BSF_LIBRARY.equals(library) ||
!file.startsWith("jq/ui") || loadJQueryUI)
createAndAddComponent(root, context, SCRIPT_RENDERER, file, library);
}
viewMap.remove(BASIC_JS_RESOURCE_KEY);
}
@SuppressWarnings("unchecked")
Map<String, String> resourceMap = (Map<String, String>) viewMap.get(RESOURCE_KEY);
if (null != resourceMap) {
for (Entry<String, String> entry : resourceMap.entrySet()) {
String file = entry.getValue();
String library = entry.getKey().substring(0, entry.getKey().length() - file.length() - 1);
if (!"jq/jquery.js".equals(file) || !C.BSF_LIBRARY.equals(library) ||
!file.startsWith("jq/ui") || loadJQueryUI)
createAndAddComponent(root, context, SCRIPT_RENDERER, file, library);
}
viewMap.remove(RESOURCE_KEY);
}
return viewMap;
}
private boolean shouldLibraryBeLoaded(String initParameter, boolean defaultValue) {
String suppressLibrary = BsfUtils.getInitParam(initParameter);
if(suppressLibrary == null)
return defaultValue;
return !isTrueOrYes(ELTools.evalAsString(suppressLibrary));
}
private void addResourceIfNecessary(UIViewRoot root, FacesContext context, InternalIE8CompatiblityLinks output) {
addResourceIfNecessary(root, context, output, output.getClass());
}
private void addResourceIfNecessary(UIViewRoot root, FacesContext context, InternalFALink output) {
addResourceIfNecessary(root, context, output, output.getClass());
}
private void addResourceIfNecessary(UIViewRoot root, FacesContext context, UIComponent output, Class<?> clazz) {
for (UIComponent c : root.getComponentResources(context, "head"))
if (c.getClass() == clazz)
return;
//resource not found yet, so add it now
root.addComponentResource(context, output, "head");
}
private void addResourceIfNecessary(UIViewRoot root, FacesContext context, UIOutput output) {
Object libToAdd = output.getAttributes().get("library");
Object nameToAdd = output.getAttributes().get("name");
for (UIComponent c : root.getComponentResources(context, "head")) {
String library = (String) c.getAttributes().get("library");
String name = (String) c.getAttributes().get("name");
if (library != null && library.equals(libToAdd) && name != null && name.equals(nameToAdd))
return;
}
root.addComponentResource(context, output, "head");
// System.out.println("++" + output.getClientId() + " " + nameToAdd + " " + libToAdd);
}
/**
* Remove duplicate resource files. For some reason, many resource files are
* added more than once, especially when AJAX is used. The method removes
* the duplicate files.
*
* TODO: Verify if the duplicate resource files are a bug of BootsFaces or
* of the Mojarra library itself.
*
* @param root
* The current UIViewRoot
* @param context
* The current FacesContext
*/
private void removeDuplicateResources(UIViewRoot root, FacesContext context) {
List<UIComponent> resourcesToRemove = new ArrayList<UIComponent>();
Map<String, UIComponent> alreadyThere = new HashMap<String, UIComponent>();
List<UIComponent> components = new ArrayList<UIComponent>(
root.getComponentResources(context, "head"));
Collections.sort(components, new Comparator<UIComponent>() {
@Override
public int compare(UIComponent o1, UIComponent o2) {
return o1.getClientId().compareTo(o2.getClientId());
}
});
for (UIComponent resource : components) {
String name = (String) resource.getAttributes().get("name");
String library = (String) resource.getAttributes().get("library");
String key = library + "/" + name + "/" + resource.getClass().getName();
if (alreadyThere.containsKey(key)) {
resourcesToRemove.add(resource);
continue;
}
alreadyThere.put(key, resource);
}
for (UIComponent c : resourcesToRemove) {
c.setInView(false);
root.removeComponentResource(context, c);
//String name = (String) c.getAttributes().get("name");
//String library = (String) c.getAttributes().get("library");
//System.out.println("-1" + c.getClientId() + " " + name + " " + library + " " + c.getClass().getSimpleName() );
}
}
/**
* Remove Bootstrap CSS files (called if the context param
* "net.bootsfaces.get_bootstrap_from_cdn" is set).
* Only needed for Default Theme (Bootstrap) and custom themes,
* other themes would get default theme look and feel otherwise.
*
* @param root
* The current UIViewRoot
* @param context
* The current FacesContext
*/
private void removeBootstrapResources(UIViewRoot root, FacesContext context) {
String theme = getTheme(context);
if(theme.equals("default")||theme.equals("other")) {
for (UIComponent resource : root.getComponentResources(context, "head")) {
String name = (String) resource.getAttributes().get("name");
String library = (String) resource.getAttributes().get("library");
if (C.BSF_LIBRARY.equals(library) && name != null ) {
if(name.endsWith("bootstrap.min.css")||name.endsWith("bootstrap.css")||name.endsWith("core.css")||name.endsWith("theme.css")) {
resource.setInView(false);
root.removeComponentResource(context, resource);
}
}
}
}
/*List<UIComponent> resourcesToRemove = new ArrayList<UIComponent>();
for (UIComponent resource : root.getComponentResources(context, "head")) {
String name = (String) resource.getAttributes().get("name");
String library = (String) resource.getAttributes().get("library");
if ("bsf".equals(library) && name != null && name.endsWith(".css")) {
resourcesToRemove.add(resource);
}
}
for (UIComponent c : resourcesToRemove) {
c.setInView(false);
root.removeComponentResource(context, c);
//System.out.println("-2" + c.getClientId());
}*/
}
/**
* Make sure jQuery is loaded before jQueryUI, and that every other
* Javascript is loaded later. Also make sure that the BootsFaces resource
* files are loaded prior to other resource files, giving the developer the
* opportunity to overwrite a CSS or JS file.
*
* @param root
* The current UIViewRoot
* @param context
* The current FacesContext
*/
private void enforceCorrectLoadOrder(UIViewRoot root, FacesContext context) {
// // first, handle the CSS files.
// // Put BootsFaces.css or BootsFaces.min.css first,
// // theme.css second
// // and everything else behind them.
List<UIComponent> resources = new ArrayList<UIComponent>();
List<UIComponent> first = new ArrayList<UIComponent>();
List<UIComponent> middle = new ArrayList<UIComponent>();
List<UIComponent> last = new ArrayList<UIComponent>();
for (UIComponent resource : root.getComponentResources(context, "head")) {
String name = (String) resource.getAttributes().get("name");
String position = (String) resource.getAttributes().get("position");
if ("first".equals(position)) {
first.add(resource);
} else if ("last".equals(position)) {
last.add(resource);
} else if ("middle".equals(position)) {
middle.add(resource);
} else {
if (name != null && (name.endsWith(".js"))) {
resources.add(resource);
}
}
}
// add the JavaScript files in correct order.
Collections.sort(resources, new ResourceFileComparator());
for (UIComponent c : first) {
root.removeComponentResource(context, c);
}
for (UIComponent c : resources) {
root.removeComponentResource(context, c);
}
for (UIComponent c : last) {
root.removeComponentResource(context, c);
}
for (UIComponent c : root.getComponentResources(context, "head")) {
middle.add(c);
}
for (UIComponent c : middle) {
root.removeComponentResource(context, c);
}
for (UIComponent c : first) {
root.addComponentResource(context, c, "head");
}
for (UIComponent c : middle) {
root.addComponentResource(context, c, "head");
}
for (UIComponent c : resources) {
root.addComponentResource(context, c, "head");
}
for (UIComponent c : last) {
root.addComponentResource(context, c, "head");
}
}
/**
* Look whether a b:iconAwesome component is used. If so, the
* font-awesome.css is removed from the resource list because it's loaded
* from the CDN.
*
* @return true, if the font-awesome.css is found in the resource list. Note
* the side effect of this method!
*/
private boolean isFontAwesomeComponentUsedAndRemoveIt() {
FacesContext fc = FacesContext.getCurrentInstance();
UIViewRoot viewRoot = fc.getViewRoot();
ListIterator<UIComponent> resourceIterator = (viewRoot.getComponentResources(fc, "head")).listIterator();
UIComponent fontAwesomeResource = null;
while (resourceIterator.hasNext()) {
UIComponent resource = resourceIterator.next();
String name = (String) resource.getAttributes().get("name");
// rw.write("\n<!-- res: '"+name+"' -->" );
if (name != null) {
if (name.endsWith("font-awesome.css"))
fontAwesomeResource = resource;
}
}
if (null != fontAwesomeResource) {
fontAwesomeResource.setInView(false);
viewRoot.removeComponentResource(fc, fontAwesomeResource);
//System.out.println("-3" + fontAwesomeResource.getClientId());
return true;
}
return false;
}
/**
* Looks for the header in the JSF tree.
*
* @param root
* The root of the JSF tree.
* @return null, if the head couldn't be found.
*/
private UIComponent findHeader(UIViewRoot root) {
for (UIComponent c : root.getChildren()) {
if (c instanceof HtmlHead)
return c;
}
for (UIComponent c : root.getChildren()) {
if (c instanceof HtmlBody)
return null;
if (c instanceof UIOutput)
if (c.getFacets() != null)
return c;
}
return null;
}
/**
* Registers a JS file that needs to be included in the header of the HTML
* file, but after jQuery and AngularJS.
*
* @param library
* The name of the sub-folder of the resources folder.
* @param resource
* The name of the resource file within the library folder.
*/
public static void addResourceToHeadButAfterJQuery(String library, String resource) {
addResource(resource, library, library + "#" + resource, RESOURCE_KEY);
}
/**
* Registers a core JS file that needs to be included in the header of the HTML
* file, but after jQuery and AngularJS.
*
* @param library
* The name of the sub-folder of the resources folder.
* @param resource
* The name of the resource file within the library folder.
*/
public static void addBasicJSResource(String library, String resource) {
addResource(resource, library, resource, BASIC_JS_RESOURCE_KEY);
}
/**
* Registers a themed CSS file that needs to be included in the header of the HTML
* file.
*
* @param resource
* The name of the resource file within the library folder.
*/
public static void addThemedCSSResource(String resource) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
@SuppressWarnings("unchecked")
List<String> resourceList = (List<String>) viewMap.get(THEME_RESOURCE_KEY);
if (null == resourceList) {
resourceList = new ArrayList<String>();
viewMap.put(THEME_RESOURCE_KEY, resourceList);
}
if (!resourceList.contains(resource)) {
resourceList.add(resource);
}
}
/**
* Registers a Extension CSS file that needs to be included in the header of the HTML
* file.
*
* @param resource
* The name of the resource file within the library folder.
*/
public static void addExtCSSResource(String resource) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
@SuppressWarnings("unchecked")
List<String> resourceList = (List<String>) viewMap.get(EXT_RESOURCE_KEY);
if (null == resourceList) {
resourceList = new ArrayList<String>();
viewMap.put(EXT_RESOURCE_KEY, resourceList);
}
if (!resourceList.contains(resource)) {
resourceList.add(resource);
}
}
private static void addResource(String resource, String library, String resourceKey, String resourceTypeKey) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
@SuppressWarnings("unchecked")
Map<String, String> resourceMap = (Map<String, String>) viewMap.get(resourceTypeKey);
if (null == resourceMap) {
resourceMap = new HashMap<String, String>();
viewMap.put(resourceTypeKey, resourceMap);
}
if (!resourceMap.containsKey(resourceKey))
resourceMap.put(library + "#" + resource, resource);
}
private String evalELIfPossible(String expression) {
if (expression != null)
expression = ELTools.evalAsString(expression).trim();
else
expression = "";
return expression;
}
private void createAndAddComponent(UIViewRoot root, FacesContext context,
String rendererType, String name, String library) {
UIOutput output = new UIOutput();
output.setRendererType(rendererType);
output.getAttributes().put("name", name);
output.getAttributes().put("library", library);
output.getAttributes().put("target", "head");
addResourceIfNecessary(root, context, output);
}
private boolean isTrueOrYes(String param) {
return param.equalsIgnoreCase("true") || param.equalsIgnoreCase("yes");
}
private boolean isFalseOrNo(String param) {
return param.equalsIgnoreCase("false") || param.equalsIgnoreCase("no");
}
}