/** * (C) 2013-2015 Stephan Rauh http://www.beyondjava.net * * 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.decorator; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.faces.context.FacesContext; import javax.faces.view.facelets.Tag; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagAttributes; import javax.faces.view.facelets.TagDecorator; import net.bootsfaces.component.ComponentsEnum; import net.bootsfaces.expressions.decorator.AFTagAttributes; import net.bootsfaces.expressions.decorator.TagAttributeUtilities; import net.bootsfaces.utils.BsfUtils; /** * This is one of the most important classes of AngularFaces. It converts attributes to pass-through parameters, adds * them to the list of JSF bean to be synchronized with the client and implements a couple of pseudo JSF tags. */ public class BootsFacesTagDecorator implements TagDecorator { private static final Logger LOGGER = Logger.getLogger(BootsFacesTagDecorator.class.getName()); private static boolean active = false; private boolean activeByDefault=true; private static final String HTML_NAMESPACE = "http://www.w3.org/1999/xhtml"; // private static final Logger LOGGER = Logger.getLogger(BootsFacesTagDecorator.class.getName()); private static final String JSF_NAMESPACE = "http://xmlns.jcp.org/jsf/html"; private static final String JSF_NAMESPACE_OLD = "http://java.sun.com/jsf/html"; private static final String JSF_CORE_NAMESPACE = "http://xmlns.jcp.org/jsf/core"; private static final String JSF_CORE_NAMESPACE_OLD = "http://java.sun.com/jsf/core"; private static final String PASS_THROUGH_NAMESPACE = "http://xmlns.jcp.org/jsf/passthrough"; private static final String BOOTSFACES_NAMESPACE = "http://bootsfaces.net/ui"; private static final Map<String, String> bootsfacesTags; private static final Map<String, Boolean> activePages = new HashMap<String, Boolean>(); static { ComponentsEnum[] components = ComponentsEnum.values(); bootsfacesTags = new HashMap<String, String>(); for (ComponentsEnum component : components) { bootsfacesTags.put(component.tagname(), component.tagname()); } LOGGER.log(Level.INFO, "The simplified HTML-like syntax of BootsFaces is available with {0} components. " + "You can switch it off globally using the context parameter net.bootsfaces.defaults.decorator in the web.xml or on a per-page basis by adding the attribute bootsFacesDecorator=''false''.", bootsfacesTags.size()); } public BootsFacesTagDecorator() { String isActive = BsfUtils.getInitParam("net.bootsfaces.defaults.decorator", FacesContext.getCurrentInstance()); if ("false".equalsIgnoreCase(isActive)) { activeByDefault=false; } else { activeByDefault=true; } } public static boolean isActive() { return active; } private final RelaxedTagDecorator relaxedDecorator = new RelaxedTagDecorator(); private Tag convertElementToInputTag(Tag tag, TagAttributes modifiedAttributes) { TagAttribute[] attributes = modifiedAttributes.getAll(); TagAttribute[] lessAttributes = Arrays.copyOf(attributes, attributes.length - 1); TagAttributes less = new AFTagAttributes(lessAttributes); Tag t = new Tag(tag.getLocation(), BOOTSFACES_NAMESPACE, "inputText", "b:inputText", less); return t; } private Tag convertElementToSelectOneMenuTag(Tag tag, TagAttributes modifiedAttributes) { TagAttribute[] attributes = modifiedAttributes.getAll(); TagAttribute[] lessAttributes = Arrays.copyOf(attributes, attributes.length - 1); TagAttributes less = new AFTagAttributes(lessAttributes); Tag t = new Tag(tag.getLocation(), BOOTSFACES_NAMESPACE, "selectOneMenu", "b:selectOneMenu", less); return t; } private Tag convertToInputTag(Tag tag, TagAttributes attributeList) { TagAttribute[] attributes = attributeList.getAll(); TagAttributes more = new AFTagAttributes(attributes); Tag t = new Tag(tag.getLocation(), BOOTSFACES_NAMESPACE, "inputText", "b:inputText", more); return t; } private Tag convertToSelectOneMenuTag(Tag tag, TagAttributes attributeList) { TagAttribute[] attributes = attributeList.getAll(); TagAttributes more = new AFTagAttributes(attributes); Tag t = new Tag(tag.getLocation(), BOOTSFACES_NAMESPACE, "selectOneMenu", "b:selectOneMenu", more); return t; } private Tag convertDivElementToPanelGroup(Tag tag, TagAttributes modifiedAttributes, boolean isDiv) { TagAttribute[] attributes = modifiedAttributes.getAll(); TagAttributes more = addBlockAttributeIfNeeded(tag, isDiv, attributes); Tag t = new Tag(tag.getLocation(), JSF_NAMESPACE, "panelGroup", "h:panelGroup", more); return t; } private Tag convertDivTagToPanelGroup(Tag tag, TagAttributes attributeList, boolean isDiv) { TagAttribute[] attributes = attributeList.getAll(); TagAttributes more = addBlockAttributeIfNeeded(tag, isDiv, attributes); Tag t = new Tag(tag.getLocation(), JSF_NAMESPACE, "panelGroup", "panelGroup", more); return t; } private TagAttributes addBlockAttributeIfNeeded(Tag tag, boolean isDiv, TagAttribute[] attributes) { TagAttribute[] moreAttributes = attributes; if (isDiv) { moreAttributes = new TagAttribute[attributes.length + 1]; for (int i = 0; i < attributes.length; i++) moreAttributes[i] = attributes[i]; moreAttributes[attributes.length] = TagAttributeUtilities.createTagAttribute(tag.getLocation(), HTML_NAMESPACE, "display", "display", "block"); } TagAttributes more = new AFTagAttributes(moreAttributes); return more; } /** * Converts <option>firstComboboxItem</option> to <f:selectItem itemValue="firstComboxItem">. */ private Tag convertTofSelectItemText(Tag tag, TagAttributes attributeList) { TagAttribute[] attributes = attributeList.getAll(); AFTagAttributes more = new AFTagAttributes(attributes); more.replaceAttribute("value", "itemValue"); more.replaceAttribute("label", "itemLabel"); Tag t = new Tag(tag.getLocation(), JSF_CORE_NAMESPACE, "selectItem", "selectItem", more); return t; } @Override public Tag decorate(Tag tag) { TagAttribute decorator = tag.getAttributes().get("bootsFacesDecorator"); String page = tag.getLocation().getPath(); if (decorator != null) { boolean decoratorActive = "true".equalsIgnoreCase(decorator.getValue()); Boolean isActive = activePages.get(page); if (isActive == null) { activePages.put(page, decoratorActive); } else if (isActive.booleanValue() != decoratorActive) { activePages.remove(page); activePages.put(page, decoratorActive); } } Boolean isActive = activePages.get(page); if (isActive == null) { isActive=activeByDefault; } if (isActive == Boolean.TRUE) { Tag newTag = createTags(tag); newTag = addSearchExpressionResolver(newTag); return newTag; } else { return tag; } } private Tag addSearchExpressionResolver(Tag tag) { if (tag.getNamespace().equals(JSF_CORE_NAMESPACE) ||tag.getNamespace().equals(JSF_CORE_NAMESPACE_OLD) ||tag.getNamespace().equals(JSF_NAMESPACE_OLD) || tag.getNamespace().equals(JSF_NAMESPACE)) { boolean changeFor = containsAdvancesSearchExpression(tag, "for"); // boolean changeRender = containsAdvancesSearchExpression(tag, "render"); // boolean changeExecute = containsAdvancesSearchExpression(tag, "execute"); if (changeFor /* || changeRender || changeExecute */) { TagAttribute[] attributes = tag.getAttributes().getAll(); AFTagAttributes more = new AFTagAttributes(attributes); if (changeFor) { String old = tag.getAttributes().get("for").getValue(); more.replaceAttributeValue("for", "#{searchExpressionResolverBean.resolve(component, '" + old + "')}"); } // if (changeRender) { // String old = tag.getAttributes().get("render").getValue(); // more.replaceAttributeValue("render", "#{searchExpressionResolverBean.resolve(component, '" + old + "')}"); // } // if (changeExecute) { // String old = tag.getAttributes().get("execute").getValue(); // more.replaceAttributeValue("execute", "#{searchExpressionResolverBean.resolve(component, '" + old + "')}"); // } tag = new Tag(tag.getLocation(), tag.getNamespace(), tag.getLocalName(), tag.getQName(), more); } } return tag; } private boolean containsAdvancesSearchExpression(Tag tag, String attribute) { boolean changeIt=false; TagAttribute forAttribute = tag.getAttributes().get(attribute); if (null != forAttribute) { String value = forAttribute.getValue(); if (value.contains("*")) changeIt=true; if (value.contains("@")) changeIt=true; if (value.equals("@form") || value.equals("@none") || value.equals("@this") || value.equals("@all")) changeIt=false; if (value.startsWith("#{")) changeIt=false; } return changeIt; } private Tag createTags(Tag tag) { String ns = tag.getNamespace(); // we only handle html tags! if (HTML_NAMESPACE.equals(ns)) { active = true; TagAttributes modifiedAttributes = tag.getAttributes(); // Apache MyFaces converts HTML tag with jsf: namespace, but missing // an // attribute, into jsf:element tag. We'll fix this // for the special case of input fields. if ("element".equals(tag.getLocalName())) { TagAttribute tagAttribute = modifiedAttributes.get(PASS_THROUGH_NAMESPACE, "elementName"); if ("input".equals(tagAttribute.getValue())) { return convertElementToInputTag(tag, modifiedAttributes); } if ("select".equals(tagAttribute.getValue())) { return convertElementToSelectOneMenuTag(tag, modifiedAttributes); } if ("div".equals(tagAttribute.getValue())) { return convertDivElementToPanelGroup(tag, modifiedAttributes, true); } } Tag newTag = relaxedDecorator.decorate(tag); if (newTag != null && newTag != tag) { return newTag; } if ("input".equals(tag.getLocalName())) { return convertToInputTag(tag, modifiedAttributes); } if ("select".equals(tag.getLocalName())) { return convertToSelectOneMenuTag(tag, modifiedAttributes); } if ("div".equals(tag.getLocalName())) { return convertDivTagToPanelGroup(tag, modifiedAttributes, true); } if ("span".equals(tag.getLocalName())) { return convertDivTagToPanelGroup(tag, modifiedAttributes, false); } if ("option".equals(tag.getLocalName())) { return convertTofSelectItemText(tag, modifiedAttributes); } tag = convertBootsFacesTag(tag); } return tag; } private Tag convertBootsFacesTag(Tag tag) { if (HTML_NAMESPACE.equals(tag.getNamespace())) { String tagname = tag.getLocalName(); if (bootsfacesTags.containsKey(tagname)) { AFTagAttributes modifiedAttributes = new AFTagAttributes(tag.getAttributes().getAll()); Tag t = new Tag(tag.getLocation(), BOOTSFACES_NAMESPACE, tag.getLocalName(), tag.getQName(), modifiedAttributes); return t; } } return tag; } }