/**
* (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.HashMap;
import java.util.Map;
import javax.faces.render.Renderer;
import javax.faces.view.Location;
import javax.faces.view.facelets.FaceletException;
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.expressions.decorator.AFTagAttributes;
import net.bootsfaces.expressions.decorator.TagAttributeUtilities;
/**
* A simple tag decorator to enable jsf: syntax
*/
public class RelaxedTagDecorator implements TagDecorator {
private static enum Mapper {
body("h:body"),
head("h:head"),
label("h:outputLabel"),
form("h:form"),
button(new ElementConverter("b:commandButton", "jsf:outcome"), new ElementConverter("b:navLink"));
private ElementConverter elementConverter;
private Mapper(final ElementConverter... elementConverters) {
if (elementConverters.length == 1) {
this.elementConverter = elementConverters[0];
} else {
this.elementConverter = new ElementConverter() {
@Override
public Tag decorate(Tag tag) {
for (ElementConverter converter : elementConverters) {
Tag decorated = converter.decorate(tag);
if (decorated != null) {
return decorated;
}
}
return null;
}
};
}
}
private Mapper(String faceletTag) {
elementConverter = new ElementConverter(faceletTag);
}
}
private static enum Namespace {
p("http://xmlns.jcp.org/jsf/passthrough"), jsf("http://xmlns.jcp.org/jsf"), h("http://java.sun.com/jsf/html"),
b("http://bootsfaces.net/ui");
private String uri;
Namespace(String uri) {
this.uri = uri;
}
}
public Tag decorate(Tag tag) {
String ns = tag.getNamespace();
// we only handle html tags!
if (!("".equals(ns) || "http://www.w3.org/1999/xhtml".equals(ns))) {
throw new FaceletException("Elements with namespace " + ns + " may not have attributes in namespace "
+ Namespace.jsf.uri + "." + " Namespace " + Namespace.jsf.uri
+ " is intended for otherwise non-JSF-aware markup, such as <input type=\"text\" jsf:id >"
+ " It is not valid to have <h:commandButton jsf:id=\"button\" />.");
}
for (Mapper mapper : Mapper.values()) {
if (tag.getLocalName().equals(mapper.name())) {
return mapper.elementConverter.decorate(tag);
}
}
return null;
}
private static class ElementConverter implements TagDecorator {
private String localName;
private Namespace namespace;
private String arbiterAttributeName;
private String arbiterAttributeNamespace = "";
private Map<String, String> additionalMappings = new HashMap<String, String>();
private String otherHtmlIdAttribute;
private ElementConverter() {
super();
}
private ElementConverter(String faceletsTag) {
this(faceletsTag, null);
}
private ElementConverter(String faceletsTag, String arbiterAttributeName) {
String[] strings = faceletsTag.split(":");
this.namespace = Namespace.valueOf(strings[0]);
this.localName = strings[1];
this.arbiterAttributeName = arbiterAttributeName;
if (arbiterAttributeName != null && arbiterAttributeName.indexOf(':') > 0) {
strings = arbiterAttributeName.split(":");
this.arbiterAttributeNamespace = Namespace.valueOf(strings[0]).uri;
this.arbiterAttributeName = strings[1];
}
}
public Tag decorate(Tag tag) {
if (arbiterAttributeName == null) {
// no arbiter
return convertTag(tag, namespace, localName);
}
TagAttribute arbiterAttribute = tag.getAttributes().get(arbiterAttributeNamespace, arbiterAttributeName);
String myLocalName=null;
if (arbiterAttribute == null) {
// no arbiter
// return null;
} else {
myLocalName = additionalMappings.get(arbiterAttribute.getValue());
}
if (myLocalName == null) {
myLocalName = this.localName;
}
return convertTag(tag, namespace, myLocalName);
}
protected Tag convertTag(Tag tag, Namespace namespace, String localName) {
Location location = tag.getLocation();
String ns = namespace.uri;
String qName = namespace.name() + ":" + localName;
TagAttributes attributes = convertAttributes(tag.getAttributes(), tag);
Tag converted = new Tag(location, ns, localName, qName, attributes);
for (TagAttribute tagAttribute : attributes.getAll()) {
// set the correct tag
tagAttribute.setTag(converted);
}
return converted;
}
protected TagAttributes convertAttributes(TagAttributes original, Tag element) {
Map<String, TagAttribute> attributes = new HashMap<String, TagAttribute>();
TagAttribute elementName = createElementName(element);
attributes.put(elementName.getQName(), elementName);
for (TagAttribute attribute : original.getAll()) {
TagAttribute converted = convertTagAttribute(attribute);
// avoid duplicates
attributes.put(converted.getQName(), converted);
}
return new AFTagAttributes(attributes.values().toArray(new TagAttribute[attributes.size()]));
}
private TagAttribute createElementName(Tag tag) {
Location location = tag.getLocation();
String ns = Namespace.p.uri;
String myLocalName = Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY;
String qName = "p:" + myLocalName;
String value = tag.getLocalName();
return TagAttributeUtilities.createTagAttribute(location, ns, myLocalName, qName, value);
}
protected TagAttribute convertTagAttribute(TagAttribute attribute) {
Location location = attribute.getLocation();
String ns = attribute.getNamespace();
String myLocalName = attribute.getLocalName();
String qName;
String value = attribute.getValue();
if (Namespace.jsf.uri.equals(attribute.getNamespace())) {
// make this a component attribute
qName = myLocalName;
ns = "";
} else {
if (ns.length() != 0 && !ns.equals(attribute.getTag().getNamespace())) {
// the attribute has a different namespace than the tag.
// preserve it.
return attribute;
}
if (attribute.getLocalName().equals(otherHtmlIdAttribute)) {
// special case for input name
qName = "id";
myLocalName = "id";
} else {
// make this a pass through attribute
qName = "h:" + myLocalName;
ns = Namespace.h.uri;
}
}
return TagAttributeUtilities.createTagAttribute(location, ns, myLocalName, qName, value);
}
}
}