You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2014/03/27 16:12:56 UTC
svn commit: r1582342 - in /myfaces/core/trunk/impl/src:
main/java/org/apache/myfaces/view/facelets/tag/
main/java/org/apache/myfaces/view/facelets/tag/jsf/html/
test/java/org/apache/myfaces/view/facelets/tag/jsf/html/
test/resources/org/apache/myfaces/...
Author: lu4242
Date: Thu Mar 27 15:12:55 2014
New Revision: 1582342
URL: http://svn.apache.org/r1582342
Log:
MYFACES-3876 - Clarify requeriments and behavior of HTML friendly markup feature
Modified:
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultTagDecorator.java
myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java
myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/jsf/html/testConvertTagAttributes1.xhtml
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java?rev=1582342&r1=1582341&r2=1582342&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java Thu Mar 27 15:12:55 2014
@@ -30,6 +30,7 @@ import javax.faces.view.facelets.Tag;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagException;
import java.beans.IntrospectionException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -37,6 +38,7 @@ import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.faces.render.Renderer;
import org.apache.myfaces.view.facelets.PassthroughRule;
import org.apache.myfaces.view.facelets.tag.jsf.PassThroughLibrary;
@@ -118,6 +120,12 @@ public final class MetaRulesetImpl exten
private final Class<?> _type;
private final List<MetaRule> _passthroughRules;
+
+ /**
+ * Indicates when a tag comes from html markup.
+ *
+ */
+ private final boolean _htmlMarkupTag;
public MetaRulesetImpl(Tag tag, Class<?> type)
{
@@ -143,6 +151,7 @@ public final class MetaRulesetImpl exten
PassThroughLibrary.NAMESPACE);
TagAttribute[] passthroughAttributeAlias = _tag.getAttributes().getAll(
PassThroughLibrary.ALIAS_NAMESPACE);
+ boolean htmlMarkupTag = false;
if (passthroughAttribute.length > 0 ||
passthroughAttributeAlias.length > 0)
@@ -160,6 +169,10 @@ public final class MetaRulesetImpl exten
{
_passthroughAttributes[i] = attribute;
i++;
+ if (Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY.equals(attribute.getLocalName()))
+ {
+ htmlMarkupTag = true;
+ }
}
else
{
@@ -176,6 +189,8 @@ public final class MetaRulesetImpl exten
_attributes.put(attribute.getLocalName(), attribute);
}
}
+
+ _htmlMarkupTag = htmlMarkupTag;
// add default rules
_rules.add(BeanPropertyTagRule.INSTANCE);
@@ -229,40 +244,100 @@ public final class MetaRulesetImpl exten
assert !_rules.isEmpty();
- if (!_attributes.isEmpty())
+ if (_htmlMarkupTag)
{
- target = this._getMetadataTarget();
- int ruleEnd = _rules.size() - 1;
-
- // now iterate over attributes
- for (Map.Entry<String, TagAttribute> entry : _attributes.entrySet())
+ // HTML markup component
+ if (!_attributes.isEmpty())
{
- Metadata data = null;
-
- int i = ruleEnd;
+ target = this._getMetadataTarget();
+ int ruleEnd = _rules.size() - 1;
+ int ptRuleEnd = _passthroughRules.size() - 1;
- // First loop is always safe
- do
+ // now iterate over attributes
+ for (Map.Entry<String, TagAttribute> entry : _attributes.entrySet())
{
- MetaRule rule = _rules.get(i);
- data = rule.applyRule(entry.getKey(), entry.getValue(), target);
- i--;
- } while (data == null && i >= 0);
+ Metadata data = null;
- if (data == null)
- {
- if (log.isLoggable(Level.SEVERE))
+ Method m = target.getWriteMethod(entry.getKey());
+
+ // if the property is writable
+ if (m != null)
+ {
+ int i = ruleEnd;
+
+ // Apply as a normal attribute
+ // First loop is always safe
+ do
+ {
+ MetaRule rule = _rules.get(i);
+ data = rule.applyRule(entry.getKey(), entry.getValue(), target);
+ i--;
+ } while (data == null && i >= 0);
+ }
+ else if (ptRuleEnd >= 0)
+ {
+ // Apply as passthrough attribute
+ int i = ptRuleEnd;
+
+ do
+ {
+ MetaRule rule = _passthroughRules.get(i);
+ data = rule.applyRule(entry.getKey(), entry.getValue(), target);
+ i--;
+ } while (data == null && i >= 0);
+ }
+
+ if (data == null)
+ {
+ if (log.isLoggable(Level.SEVERE))
+ {
+ log.severe(entry.getValue() + " Unhandled by MetaTagHandler for type " + _type.getName());
+ }
+ }
+ else
{
- log.severe(entry.getValue() + " Unhandled by MetaTagHandler for type " + _type.getName());
+ _mappers.add(data);
}
}
- else
+ }
+ }
+ else
+ {
+ if (!_attributes.isEmpty())
+ {
+ target = this._getMetadataTarget();
+ int ruleEnd = _rules.size() - 1;
+
+ // now iterate over attributes
+ for (Map.Entry<String, TagAttribute> entry : _attributes.entrySet())
{
- _mappers.add(data);
+ Metadata data = null;
+
+ int i = ruleEnd;
+
+ // First loop is always safe
+ do
+ {
+ MetaRule rule = _rules.get(i);
+ data = rule.applyRule(entry.getKey(), entry.getValue(), target);
+ i--;
+ } while (data == null && i >= 0);
+
+ if (data == null)
+ {
+ if (log.isLoggable(Level.SEVERE))
+ {
+ log.severe(entry.getValue() + " Unhandled by MetaTagHandler for type " + _type.getName());
+ }
+ }
+ else
+ {
+ _mappers.add(data);
+ }
}
}
}
-
+
if (_passthroughAttributes.length > 0 &&
_passthroughRules.size() > 0)
{
@@ -302,7 +377,7 @@ public final class MetaRulesetImpl exten
}
}
}
-
+
if (_mappers.isEmpty())
{
return NONE;
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultTagDecorator.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultTagDecorator.java?rev=1582342&r1=1582341&r2=1582342&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultTagDecorator.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultTagDecorator.java Thu Mar 27 15:12:55 2014
@@ -261,10 +261,24 @@ public class DefaultTagDecorator impleme
private TagAttributes convertTagAttributes(Tag tag)
{
+ TagAttribute[] sourceTagAttributes = tag.getAttributes().getAll();
+
+ String elementNameTagLocalName = tag.getLocalName();
+ if ("element".equals(tag.getLocalName()) &&
+ JSF_NAMESPACE.equals(tag.getNamespace()) || JSF_ALIAS_NAMESPACE.equals(tag.getNamespace()))
+ {
+ // In jsf:element tag, the attribute elementName is required, so from this point we can
+ // assume that elementName has been set. But we need to enable the special treatement
+ // for attribute in jsf:element, so the way to do it is
+ TagAttribute elemNameAttr = tag.getAttributes().get(Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY);
+ if (elemNameAttr != null)
+ {
+ elementNameTagLocalName = elemNameAttr.getValue();
+ }
+ }
TagAttribute elementNameTagAttribute = new TagAttributeImpl(
tag.getLocation(), PASS_THROUGH_NAMESPACE , Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY,
- P_ELEMENTNAME, tag.getLocalName() );
- TagAttribute[] sourceTagAttributes = tag.getAttributes().getAll();
+ P_ELEMENTNAME, elementNameTagLocalName );
// FIXME: Doing a black box test over Mojarra it was found that passthrough
// attributes are added to both passthrough map and normal attribute map. It seems
@@ -275,6 +289,7 @@ public class DefaultTagDecorator impleme
// 1. Count how many attributes requires to be duplicated
int duplicateCount = 0;
+ /*
for (int i = 0; i < sourceTagAttributes.length; i++)
{
TagAttribute tagAttribute = sourceTagAttributes[i];
@@ -295,12 +310,13 @@ public class DefaultTagDecorator impleme
{
duplicateCount++;
}
- }
+ }*/
TagAttribute[] convertedTagAttributes = new TagAttribute[
sourceTagAttributes.length+1+duplicateCount];
boolean elementNameTagAttributeSet = false;
int j = 0;
+
for (int i = 0; i < sourceTagAttributes.length; i++)
{
TagAttribute tagAttribute = sourceTagAttributes[i];
@@ -319,35 +335,54 @@ public class DefaultTagDecorator impleme
convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
convertedNamespace, tagAttribute.getLocalName(), qname, tagAttribute.getValue());
+ /*
if (!isReservedJSFAttribute(qname))
{
j++;
// Duplicate passthrough
+ // -= Leonardo Uribe =- After discussion with Ed Burns and Frank Caputo, the reason is jsf
+ // namespace in an attribute is used to convert the tag into jsf:element, so there are cases
+ // where you want the attribute to be passed into the passthrough attribute map instead the
+ // normal attribute map. It is not expected to override jsf:element because this is a
+ // special component used to output markup, so it has been defined the attribute names that
+ // does not require this duplication. It was also found that for the remaining cases, copy
+ // the passthrough attribute does not have any effect, because by effect of the renderer
+ // associated to the component these attribute are passed through by the renderer code and
+ // time later when the passthrough attributes are rendered, there is a check that indicates
+ // that the attribute has been already rendered.
convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
PASS_THROUGH_NAMESPACE, tagAttribute.getLocalName(),
"p:"+tagAttribute.getLocalName(), tagAttribute.getValue());
- }
+ }*/
}
else if (namespace == null)
{
// should not happen, but let it because org.xml.sax.Attributes considers it
+ // -= Leonardo Uribe =- after conversation with Frank Caputo, who was the main contributor for
+ // this feature in JSF 2.2, he said that if the namespace is empty the intention is pass the
+ // attribute to the passthrough attribute map, so there is an error in the spec documentation.
convertedTagAttributes[j] = tagAttribute;
+ /*
j++;
// Duplicate passthrough
convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
PASS_THROUGH_NAMESPACE, tagAttribute.getLocalName(),
- "p:"+tagAttribute.getLocalName(), tagAttribute.getValue());
+ "p:"+tagAttribute.getLocalName(), tagAttribute.getValue());*/
}
else if (tagAttribute.getNamespace().length() == 0)
{
// "... If the current attribute's namespace is empty
// let the current attribute be convertedTagAttribute. ..."
+ // -= Leonardo Uribe =- after conversation with Frank Caputo, who was the main contributor for
+ // this feature in JSF 2.2, he said that if the namespace is empty the intention is pass the
+ // attribute to the passthrough attribute map, so there is an error in the spec documentation.
convertedTagAttributes[j] = tagAttribute;
+ /*
j++;
// Duplicate passthrough
convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
PASS_THROUGH_NAMESPACE, tagAttribute.getLocalName(),
- "p:"+tagAttribute.getLocalName(), tagAttribute.getValue());
+ "p:"+tagAttribute.getLocalName(), tagAttribute.getValue());*/
}
else if (!tag.getNamespace().equals(tagAttribute.getNamespace()))
{
Modified: myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java?rev=1582342&r1=1582341&r2=1582342&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java (original)
+++ myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/view/facelets/tag/jsf/html/DefaultHtmlDecoratorTestCase.java Thu Mar 27 15:12:55 2014
@@ -336,13 +336,21 @@ public class DefaultHtmlDecoratorTestCas
UIViewRoot root = facesContext.getViewRoot();
vdl.buildView(facesContext, root, "testConvertTagAttributes1.xhtml");
+ //<input jsf:id="box1" type="text"
+ // jsf:value="#{test.value}" jsf:customAttr="SomeValue"
+ // onclick="alert('hello')"
+ // placeholder="Enter text"
+ // pt:data_up="Going Up"/>
UIInput input1 = (UIInput) root.findComponent("myForm:box1");
Assert.assertNotNull(input1);
Assert.assertEquals(input1.getPassThroughAttributes().get("placeholder"), "Enter text");
- Assert.assertEquals(input1.getAttributes().get("placeholder"), "Enter text");
+ //Assert.assertEquals(input1.getAttributes().get("placeholder"), "Enter text");
+ Assert.assertNull(input1.getAttributes().get("placeholder"));
- Assert.assertEquals(input1.getAttributes().get("customAttr"), "SomeValue");
+
+ //Assert.assertEquals(input1.getAttributes().get("customAttr"), "SomeValue");
+ Assert.assertNull(input1.getAttributes().get("customAttr"));
// Attributes outside "id", "binding", "rendered" or "transient" can be
// copied on passthrough attribute map.
Assert.assertEquals(input1.getPassThroughAttributes().get("customAttr"), "SomeValue");
@@ -351,60 +359,92 @@ public class DefaultHtmlDecoratorTestCas
Assert.assertNull(input1.getAttributes().get("data_up"));
Assert.assertNotNull(input1.getValueExpression("value"));
- Assert.assertNotNull(input1.getPassThroughAttributes().get("value"));
+ //Assert.assertNotNull(input1.getPassThroughAttributes().get("value"));
+ Assert.assertNull(input1.getPassThroughAttributes().get("value"));
Assert.assertEquals(input1.getValue(), "value1");
Assert.assertEquals(input1.getAttributes().get("value"), "value1");
+ //<input jsf:id="box2" pt:elementName="meter"
+ // jsf:value="#{test.value}" jsf:customAttr="SomeValue"
+ // onclick="alert('hello')"
+ // placeholder="Enter text"
+ // pt:data_up="Going Up">Hello World!</input>
UIComponent input2 = root.findComponent("myForm:box2");
Assert.assertFalse(input2 instanceof UIInput);
Assert.assertEquals(input2.getRendererType(), "javax.faces.passthrough.Element");
Assert.assertEquals(input2.getPassThroughAttributes().get("placeholder"), "Enter text");
- Assert.assertEquals(input2.getAttributes().get("placeholder"), "Enter text");
+ //Assert.assertEquals(input2.getAttributes().get("placeholder"), "Enter text");
+ Assert.assertNull(input2.getAttributes().get("placeholder"));
- Assert.assertEquals(input2.getAttributes().get("customAttr"), "SomeValue");
+ //Assert.assertEquals(input2.getAttributes().get("customAttr"), "SomeValue");
+ Assert.assertNull(input2.getAttributes().get("customAttr"));
Assert.assertEquals(input2.getPassThroughAttributes().get("customAttr"), "SomeValue");
Assert.assertEquals(input2.getPassThroughAttributes().get("data_up"), "Going Up");
Assert.assertNull(input2.getAttributes().get("data_up"));
- Assert.assertNotNull(input2.getValueExpression("value"));
+ // note there is no type attribute, so it is translated into a jsf:element, and in that
+ // component, "value" is not defined, so it is set as passthrough
+ Assert.assertNull(input2.getValueExpression("value"));
Assert.assertNotNull(input2.getPassThroughAttributes().get("value"));
- Assert.assertEquals(input2.getAttributes().get("value"), "value1");
+ Assert.assertNull(input2.getAttributes().get("value"));
+ //<jsf:element id="box3" elementName="meter"
+ // value="#{test.value}" jsf:customAttr="SomeValue"
+ // onclick="alert('hello')"
+ // placeholder="Enter text"
+ // pt:data_up="Going Up">
+ // Hello Element!
+ //</jsf:element>
UIComponent input3 = root.findComponent("myForm:box3");
Assert.assertFalse(input3 instanceof UIInput);
Assert.assertEquals(input3.getRendererType(), "javax.faces.passthrough.Element");
Assert.assertEquals(input3.getPassThroughAttributes().get("placeholder"), "Enter text");
- Assert.assertEquals(input3.getAttributes().get("placeholder"), "Enter text");
+ Assert.assertNull(input3.getAttributes().get("placeholder"));
- Assert.assertEquals(input2.getAttributes().get("customAttr"), "SomeValue");
- Assert.assertEquals(input2.getPassThroughAttributes().get("customAttr"), "SomeValue");
+ Assert.assertNull(input3.getAttributes().get("customAttr"));
+ Assert.assertEquals(input3.getPassThroughAttributes().get("customAttr"), "SomeValue");
Assert.assertEquals(input3.getPassThroughAttributes().get("data_up"), "Going Up");
Assert.assertNull(input3.getAttributes().get("data_up"));
- Assert.assertNotNull(input3.getValueExpression("value"));
+ Assert.assertNull(input3.getValueExpression("value"));
Assert.assertNotNull(input3.getPassThroughAttributes().get("value"));
- Assert.assertEquals(input3.getAttributes().get("value"), "value1");
+ Assert.assertNull(input3.getAttributes().get("value"));
//Assert.assertEquals(input2.getPassThroughAttributes().get("elementName"), "meter");
+ //<h:panelGroup id="box4">
+ //<div jsf:class="noprint">
+ // MYBOX4
+ //</div>
+ //</h:panelGroup>
UIComponent box4 = root.findComponent("myForm:box4");
Assert.assertNotNull(box4);
UIComponent boxDiv4 = box4.getChildren().get(0);
Assert.assertNotNull(boxDiv4);
Assert.assertEquals(boxDiv4.getAttributes().get("styleClass"), "noprint");
- Assert.assertEquals(boxDiv4.getPassThroughAttributes().get("class"), "noprint");
+ //Assert.assertEquals(boxDiv4.getPassThroughAttributes().get("class"), "noprint");
+ Assert.assertNull(boxDiv4.getPassThroughAttributes().get("class"));
+ //<h:panelGroup id="box5">
+ //<div jsf:style="noprint">
+ // MYBOX5
+ //</div>
+ //</h:panelGroup>
UIComponent box5 = root.findComponent("myForm:box5");
Assert.assertNotNull(box5);
UIComponent boxDiv5 = box5.getChildren().get(0);
Assert.assertNotNull(boxDiv5);
- Assert.assertEquals(boxDiv5.getAttributes().get("style"), "noprint");
+ Assert.assertNull(boxDiv5.getAttributes().get("style"));
Assert.assertEquals(boxDiv5.getPassThroughAttributes().get("style"), "noprint");
+ UIComponent box6 = root.findComponent("myForm:box6");
+ Assert.assertNotNull(box6);
+ Assert.assertEquals(box6.getAttributes().get("onclick"), "alert('hello')");
+
StringWriter sw = new StringWriter();
ResponseWriter mrw = new HtmlResponseWriterImpl(sw, "text/html", "UTF-8");
@@ -518,5 +558,45 @@ public class DefaultHtmlDecoratorTestCas
Assert.assertTrue(sw.toString().contains("MYBOX5"));
Assert.assertTrue(sw.toString().contains("<div "));
Assert.assertTrue(sw.toString().contains("</div>"));
+
+ // TEST 6
+
+ sw = new StringWriter();
+ mrw = new HtmlResponseWriterImpl(sw, "text/html", "UTF-8");
+ facesContext.setResponseWriter(mrw);
+
+ box6.encodeAll(facesContext);
+
+ sw.flush();
+
+ //<div jsf:id="box6" onclick="alert('hello')">
+ // <f:ajax event="click" render="box5"/>
+ // MYBOX6
+ //</div>
+ // Try second time, to avoid the script section by f:ajax effect
+ sw = new StringWriter();
+ mrw = new HtmlResponseWriterImpl(sw, "text/html", "UTF-8");
+ facesContext.setResponseWriter(mrw);
+
+ box6.encodeAll(facesContext);
+
+ sw.flush();
+ attrs = new HtmlRenderedAttr[]{
+ new HtmlRenderedAttr("onclick",
+ "jsf.util.chain(document.getElementById('myForm:box6'), event,'alert(\\'hello\\')', "
+ + "'jsf.ajax.request(\\'myForm:box6\\',event,{render:\\'myForm:box5 \\',"
+ + "\\'javax.faces.behavior.event\\':\\'click\\'})');"),
+ };
+
+
+
+ HtmlCheckAttributesUtil.checkRenderedAttributes(attrs, sw.toString());
+ if(HtmlCheckAttributesUtil.hasFailedAttrRender(attrs))
+ {
+ Assert.fail(HtmlCheckAttributesUtil.constructErrorMessage(attrs, sw.toString()));
+ }
+ Assert.assertTrue(sw.toString().contains("MYBOX6"));
+ Assert.assertTrue(sw.toString().contains("<div "));
+ Assert.assertTrue(sw.toString().contains("</div>"));
}
}
Modified: myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/jsf/html/testConvertTagAttributes1.xhtml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/jsf/html/testConvertTagAttributes1.xhtml?rev=1582342&r1=1582341&r2=1582342&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/jsf/html/testConvertTagAttributes1.xhtml (original)
+++ myfaces/core/trunk/impl/src/test/resources/org/apache/myfaces/view/facelets/tag/jsf/html/testConvertTagAttributes1.xhtml Thu Mar 27 15:12:55 2014
@@ -54,6 +54,11 @@
MYBOX5
</div>
</h:panelGroup>
+
+ <div jsf:id="box6" onclick="alert('hello')">
+ <f:ajax event="click" render="box5"/>
+ MYBOX6
+ </div>
</form>
</body>
</html>
\ No newline at end of file