You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2021/02/28 18:43:35 UTC

[tapestry-5] 01/02: TAP5-2664: PropertyShadowBuilder fails when interface inherits same method more than once

This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch 5.6.x
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit bcd42f6b008e7a27aff64836cd235e6b7559e4f2
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Sun Feb 28 15:39:44 2021 -0300

    TAP5-2664: PropertyShadowBuilder fails when interface inherits same
    method more than once
    
    Thanks Hendrik Noot for noticing the problem and providing the patch
    used to fix it!
---
 .../META-INF/modules/t5/core/ajax.html             | 316 +++++++++++++++++++++
 .../services/PropertyShadowBuilderImpl.java        |  63 +++-
 2 files changed, 377 insertions(+), 2 deletions(-)

diff --git a/docs/home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.html b/docs/home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.html
new file mode 100644
index 0000000..08791ee
--- /dev/null
+++ b/docs/home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.html
@@ -0,0 +1,316 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+  <title>ajax.coffee</title>
+  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+  <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
+  <link rel="stylesheet" media="all" href="../../../../../../../../../../../../docco.css" />
+</head>
+<body>
+  <div id="container">
+    <div id="background"></div>
+    
+      <ul id="jump_to">
+        <li>
+          <a class="large" href="javascript:void(0);">Jump To &hellip;</a>
+          <a class="small" href="javascript:void(0);">+</a>
+          <div id="jump_wrapper">
+          <div id="jump_page_wrapper">
+            <div id="jump_page">
+              
+                
+                <a class="source" href="../../../../../../../../tapestry-beanvalidator/src/main/coffeescript/META-INF/modules/t5/beanvalidator/beanvalidator-validation.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-beanvalidator/src/main/coffeescript/META-INF/modules/t5/beanvalidator/beanvalidator-validation.coffee
+                </a>
+              
+                
+                <a class="source" href="../../../../../../../build/postprocessed-coffeescript/org/apache/tapestry5/t5-core-dom-jquery.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/build/postprocessed-coffeescript/org/apache/tapestry5/t5-core-dom-jquery.coffee
+                </a>
+              
+                
+                <a class="source" href="../../../../../../../build/postprocessed-coffeescript/org/apache/tapestry5/t5-core-dom-prototype.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/build/postprocessed-coffeescript/org/apache/tapestry5/t5-core-dom-prototype.coffee
+                </a>
+              
+                
+                <a class="source" href="ajax.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajax.coffee
+                </a>
+              
+                
+                <a class="source" href="ajaxformloop.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/ajaxformloop.coffee
+                </a>
+              
+                
+                <a class="source" href="alert.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/alert.coffee
+                </a>
+              
+                
+                <a class="source" href="autocomplete.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/autocomplete.coffee
+                </a>
+              
+                
+                <a class="source" href="bootstrap.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/bootstrap.coffee
+                </a>
+              
+                
+                <a class="source" href="confirm-click.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/confirm-click.coffee
+                </a>
+              
+                
+                <a class="source" href="console.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/console.coffee
+                </a>
+              
+                
+                <a class="source" href="datefield.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/datefield.coffee
+                </a>
+              
+                
+                <a class="source" href="events.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/events.coffee
+                </a>
+              
+                
+                <a class="source" href="exception-display.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/exception-display.coffee
+                </a>
+              
+                
+                <a class="source" href="exception-frame.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/exception-frame.coffee
+                </a>
+              
+                
+                <a class="source" href="fields.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee
+                </a>
+              
+                
+                <a class="source" href="form-fragment.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/form-fragment.coffee
+                </a>
+              
+                
+                <a class="source" href="forms.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/forms.coffee
+                </a>
+              
+                
+                <a class="source" href="init.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/init.coffee
+                </a>
+              
+                
+                <a class="source" href="localdate.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/localdate.coffee
+                </a>
+              
+                
+                <a class="source" href="messages.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/messages.coffee
+                </a>
+              
+                
+                <a class="source" href="moment.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/moment.coffee
+                </a>
+              
+                
+                <a class="source" href="pageinit.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/pageinit.coffee
+                </a>
+              
+                
+                <a class="source" href="palette.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/palette.coffee
+                </a>
+              
+                
+                <a class="source" href="select.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/select.coffee
+                </a>
+              
+                
+                <a class="source" href="time-interval.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/time-interval.coffee
+                </a>
+              
+                
+                <a class="source" href="tree.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/tree.coffee
+                </a>
+              
+                
+                <a class="source" href="utils.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/utils.coffee
+                </a>
+              
+                
+                <a class="source" href="validation.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/validation.coffee
+                </a>
+              
+                
+                <a class="source" href="zone-refresh.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/zone-refresh.coffee
+                </a>
+              
+                
+                <a class="source" href="zone.html">
+                  /home/thiago/workspace-tapestry/tapestry-5/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/zone.coffee
+                </a>
+              
+            </div>
+          </div>
+        </li>
+      </ul>
+    
+    <ul class="sections">
+        
+          <li id="title">
+              <div class="annotation">
+                  <h1>ajax.coffee</h1>
+              </div>
+          </li>
+        
+        
+        
+        <li id="section-1">
+            <div class="annotation">
+              
+              <div class="pilwrap ">
+                <a class="pilcrow" href="#section-1">&#182;</a>
+              </div>
+              <p>Copyright 2012-2014 The Apache Software Foundation</p>
+<p>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</p>
+<pre><code>http:<span class="hljs-comment">#www.apache.org/licenses/LICENSE-2.0</span></code></pre>
+<p>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.</p>
+
+            </div>
+            
+        </li>
+        
+        
+        <li id="section-2">
+            <div class="annotation">
+              
+              <div class="pilwrap ">
+                <a class="pilcrow" href="#section-2">&#182;</a>
+              </div>
+              <h2 id="t5coreajax">t5/core/ajax</h2>
+<p>Exports a single function, that invokes <code>t5/core/dom:ajaxRequest()</code> with the provided <code>url</code> and a modified version of the
+<code>options</code>.</p>
+<ul>
+<li>options.method - “post”, “get”, etc., default: “post”.</li>
+<li>options.element - if provided, the URL will be treated as a server-side event name
+ and the actual URL to be used will be obtained from dom.getEventUrl(url, element)</li>
+<li>options.contentType - request content, defaults to “application/x-www-form-urlencoded”</li>
+<li>options.data - optional, additional key/value pairs (for the default content type)</li>
+<li>options.success - handler to invoke on success. Passed the ResponseWrapper object.
+Default does nothing.</li>
+<li>options.failure - handler to invoke on failure (server responds with a non-2xx code).
+Passed the response. Default will throw the exception</li>
+<li>options.exception - handler to invoke when an exception occurs (often means the server is unavailable).
+Passed the exception. Default will generate an exception message and throw an <code>Error</code>.
+Note: not really supported under jQuery, a hold-over from Prototype.</li>
+<li>options.complete - handler to invoke after success, falure, or exception. The handler is passed no
+parameters.</li>
+</ul>
+<p>It wraps (or provides) <code>success</code>, <code>exception</code>, and <code>failure</code> handlers, extended to handle a partial page render
+response (for success), or properly log a server-side failure or client-side exception, including using the
+<code>t5/core/exception-frame</code> module to display a server-side processing exception.</p>
+
+            </div>
+            
+            <div class="content"><div class='highlight'><pre>define [<span class="hljs-string">&quot;./pageinit&quot;</span>, <span class="hljs-string">&quot;./dom&quot;</span>, <span class="hljs-string">&quot;./exception-frame&quot;</span>, <span class="hljs-string">&quot;./console&quot;</span>, <span class="hljs-string">&quot;underscore&quot;</span>],
+  <span class="hljs-function"><span class="hljs-params">(pageinit, dom, exceptionframe, <span class="hljs-built_in">console</span>, _)</span> -&gt;</span>
+    (url, options) -&gt;
+<span class="hljs-function">
+      <span class="hljs-title">complete</span> = -&gt;</span>
+        <span class="hljs-keyword">if</span> options.complete
+          options.complete()
+
+        <span class="hljs-keyword">return</span>
+        
+      <span class="hljs-keyword">if</span> options.hasOwnProperty <span class="hljs-string">&#x27;element&#x27;</span>
+        url = dom.getEventUrl(url, options.element)
+
+      newOptions = _.extend {}, options,</pre></div></div>
+            
+        </li>
+        
+        
+        <li id="section-3">
+            <div class="annotation">
+              
+              <div class="pilwrap ">
+                <a class="pilcrow" href="#section-3">&#182;</a>
+              </div>
+              <p>Logs the exception to the console before passing it to the
+provided exception handler or throwing the exception.</p>
+
+            </div>
+            
+            <div class="content"><div class='highlight'><pre>        exception: <span class="hljs-function"><span class="hljs-params">(exception)</span> -&gt;</span>
+          <span class="hljs-built_in">console</span>.error <span class="hljs-string">&quot;Request to <span class="hljs-subst">#{url}</span> failed with <span class="hljs-subst">#{exception}</span>&quot;</span>
+
+          <span class="hljs-keyword">if</span> options.exception
+            options.exception exception
+          <span class="hljs-keyword">else</span>
+            <span class="hljs-keyword">throw</span> exception
+
+          complete()
+
+          <span class="hljs-keyword">return</span>
+
+        failure: <span class="hljs-function"><span class="hljs-params">(response, failureMessage)</span> -&gt;</span>
+          raw = response.header <span class="hljs-string">&quot;X-Tapestry-ErrorMessage&quot;</span>
+          <span class="hljs-keyword">unless</span> _.isEmpty raw
+            message = <span class="hljs-built_in">window</span>.<span class="hljs-built_in">unescape</span> raw
+            <span class="hljs-built_in">console</span>.error <span class="hljs-string">&quot;Request to <span class="hljs-subst">#{url}</span> failed with &#x27;<span class="hljs-subst">#{message}</span>&#x27;.&quot;</span>
+
+            contentType = response.header <span class="hljs-string">&quot;content-type&quot;</span>
+
+            isHTML = contentType <span class="hljs-keyword">and</span> (contentType.split(<span class="hljs-string">&#x27;;&#x27;</span>)[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">&quot;text/html&quot;</span>)
+
+            <span class="hljs-keyword">if</span> isHTML
+              exceptionframe response.text
+          <span class="hljs-keyword">else</span>
+            <span class="hljs-built_in">console</span>.error failureMessage
+
+          options.failure <span class="hljs-keyword">and</span> options.failure(response)
+
+          complete()
+
+          <span class="hljs-keyword">return</span>
+
+        success: <span class="hljs-function"><span class="hljs-params">(response)</span> -&gt;</span>
+          pageinit.handlePartialPageRenderResponse response, options.success
+
+          complete()
+
+          <span class="hljs-keyword">return</span>
+
+      dom.ajaxRequest url, newOptions</pre></div></div>
+            
+        </li>
+        
+    </ul>
+  </div>
+</body>
+</html>
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyShadowBuilderImpl.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyShadowBuilderImpl.java
index 9011b6e..51b1fa3 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyShadowBuilderImpl.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyShadowBuilderImpl.java
@@ -18,12 +18,21 @@ import org.apache.tapestry5.ioc.services.*;
 import org.apache.tapestry5.plastic.*;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
 
 public class PropertyShadowBuilderImpl implements PropertyShadowBuilder
 {
     private final PropertyAccess propertyAccess;
 
     private final PlasticProxyFactory proxyFactory;
+    
+    final private static MethodSignatureUniqueComparator METHOD_COMPARATOR = new MethodSignatureUniqueComparator();
 
     public PropertyShadowBuilderImpl(@Builtin
                                      PlasticProxyFactory proxyFactory,
@@ -96,9 +105,13 @@ public class PropertyShadowBuilderImpl implements PropertyShadowBuilder
                     }
                 });
 
-                for (Method m : propertyType.getMethods())
+                for (Method m : METHOD_COMPARATOR.getUniqueMethods(propertyType)) 
                 {
-                    plasticClass.introduceMethod(m).delegateTo(delegateMethod);
+                    final MethodDescription description = new MethodDescription(m);
+                    if (Modifier.isStatic(description.modifiers)) {
+                        continue;
+                    }
+                    plasticClass.introduceMethod(description).delegateTo(delegateMethod);
                 }
 
                 plasticClass.addToString(String.format("<Shadow: property %s of %s>", propertyName, source));
@@ -107,4 +120,50 @@ public class PropertyShadowBuilderImpl implements PropertyShadowBuilder
 
         return propertyType.cast(instantiator.newInstance());
     }
+    
+    private final static class MethodSignatureUniqueComparator implements Comparator<Method> {
+
+        @Override
+        public int compare(Method o1, Method o2) {
+
+            int comparison = o1.getName().compareTo(o2.getName());
+
+            if (comparison == 0) {
+                comparison = o1.getParameterTypes().length - o2.getParameterTypes().length;
+            }
+
+            if (comparison == 0) {
+                final int count = o1.getParameterTypes().length;
+                for (int i = 0; i < count; i++) {
+                    Class p1 = o1.getParameterTypes()[i];
+                    Class p2 = o2.getParameterTypes()[i];
+                    if (!p1.equals(p2)) {
+                        comparison = p1.getName().compareTo(p2.getName());
+                        break;
+                    }
+                }
+            }
+
+            return comparison;
+        }
+
+        public List<Method> getUniqueMethods(Class interfaceType) 
+        {
+            final List<Method> unique = new ArrayList<>(Arrays.asList(interfaceType.getMethods()));
+            Collections.sort(unique, this);
+            Method last = null;
+            Iterator<Method> iterator = unique.iterator();
+            while (iterator.hasNext()) 
+            {
+                Method m = iterator.next();
+                if (last != null && compare(m, last) == 0) 
+                {
+                    iterator.remove();
+                }
+                last = m;
+            }
+            return unique;
+        }
+
+    }
 }