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 …</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">¶</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">¶</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">"./pageinit"</span>, <span class="hljs-string">"./dom"</span>, <span class="hljs-string">"./exception-frame"</span>, <span class="hljs-string">"./console"</span>, <span class="hljs-string">"underscore"</span>],
+ <span class="hljs-function"><span class="hljs-params">(pageinit, dom, exceptionframe, <span class="hljs-built_in">console</span>, _)</span> -></span>
+ (url, options) ->
+<span class="hljs-function">
+ <span class="hljs-title">complete</span> = -></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">'element'</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">¶</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> -></span>
+ <span class="hljs-built_in">console</span>.error <span class="hljs-string">"Request to <span class="hljs-subst">#{url}</span> failed with <span class="hljs-subst">#{exception}</span>"</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> -></span>
+ raw = response.header <span class="hljs-string">"X-Tapestry-ErrorMessage"</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">"Request to <span class="hljs-subst">#{url}</span> failed with '<span class="hljs-subst">#{message}</span>'."</span>
+
+ contentType = response.header <span class="hljs-string">"content-type"</span>
+
+ isHTML = contentType <span class="hljs-keyword">and</span> (contentType.split(<span class="hljs-string">';'</span>)[<span class="hljs-number">0</span>] <span class="hljs-keyword">is</span> <span class="hljs-string">"text/html"</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> -></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;
+ }
+
+ }
}