From users-return-3917-archive-asf-public=cust-asf.ponee.io@groovy.apache.org Wed Feb 6 01:39:17 2019 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id A8A66180608 for ; Wed, 6 Feb 2019 02:39:16 +0100 (CET) Received: (qmail 23843 invoked by uid 500); 6 Feb 2019 01:39:15 -0000 Mailing-List: contact users-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: users@groovy.apache.org Delivered-To: mailing list users@groovy.apache.org Received: (qmail 23833 invoked by uid 99); 6 Feb 2019 01:39:15 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 06 Feb 2019 01:39:15 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 3812FC04F3 for ; Wed, 6 Feb 2019 01:39:15 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 2.009 X-Spam-Level: ** X-Spam-Status: No, score=2.009 tagged_above=-999 required=6.31 tests=[DKIMWL_WL_MED=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, HTML_MESSAGE=2, RCVD_IN_DNSWL_NONE=-0.0001, T_REMOTE_IMAGE=0.01] autolearn=disabled Authentication-Results: spamd4-us-west.apache.org (amavisd-new); dkim=pass (2048-bit key) header.d=asert-com-au.20150623.gappssmtp.com Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id VCqD0v82HJ1o for ; Wed, 6 Feb 2019 01:39:12 +0000 (UTC) Received: from mail-ed1-f44.google.com (mail-ed1-f44.google.com [209.85.208.44]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTPS id 334A35F175 for ; Wed, 6 Feb 2019 01:39:08 +0000 (UTC) Received: by mail-ed1-f44.google.com with SMTP id g22so4592008edr.7 for ; Tue, 05 Feb 2019 17:39:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=asert-com-au.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:reply-to:from:date:message-id :subject:to:cc; bh=bY2ezTcqM1Ei53DJuP7UIuNE1HpG7Doniu1d+FTdLuc=; b=RVGDGVEhCW6lV65iSQ/nqAUDsL8FeC0Xz+udlnnXYeQPwntMkodUMx3EOq0PuDHZat g44llmX1Z9GtkfOpCXtKK+BOHAXJauLmQo8cWfjkjJks8pWccUmxNWbmD4JRW0bmkeFv vdvcUdsKmVjVn+iBkHCAIu/yH1HtB7X/DQ27Uga5rsGgU7vcHrgOKeWzUzGIS3Gw/yLQ owMY5lThUgCNbqo7Pz+CwlKhU71lNHf71psAURAUTpf4yH3myYqsiYxsLICqG9o8mLAE 0651LVkwcksmVcL9zksDxY7QxyBiYj1N68W4AhsFbxS+EwgRp6ITAFf7d0kc62a+T7sJ nhPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:reply-to :from:date:message-id:subject:to:cc; bh=bY2ezTcqM1Ei53DJuP7UIuNE1HpG7Doniu1d+FTdLuc=; b=PxAsWsGJBFLv6MXe7gfAvGYvf4k/tE7tjZWDmYRWTKHNfb22vIhYZNDdIHmSoWxrki vq7Yo4QGumiz5Fhq3jtgZaoGwre8GltdCP4S5sKcF51/+LeGEJOyW9OKnh3U/KoZ/Jhw 4NzzB2FfZjZrHYr8sDAUgV77oNqqrS9ZNtMeROK+1s/0hdQfjzq95fbkyL1KIuXwvKfw dyGXIJq7z9MvoLSguqmogf4R4bteWWkcWRxqRdIPn9exFpi+yww4wDQqd8M7pe6YIvsT dDwjCzkLPCHD5e0m52Y0W3zVTVioAw1HFMZr+LF3eAz22gtPxz/DyjBWKbFOdxJmXbHv USzQ== X-Gm-Message-State: AHQUAuYFuLMBoWgOPIrBFZq4JesnLKAgOgv9RKhpDkrtjarXNvqLKTtH 7alK/Qzu+ys/+ql8DuBhYgc7FvID3PkFQePCe5P+dw== X-Google-Smtp-Source: AHgI3IbjE4Ez38coLBAw5qvo/4MZF5nJInnqC10vXoSfjMJlVeda97F05rRoME1G2XQY3iEjPGnS+lpV4PFd74qgRjc= X-Received: by 2002:a05:6402:396:: with SMTP id o22mr4353594edv.32.1549417147663; Tue, 05 Feb 2019 17:39:07 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: Reply-To: paulk@asert.com.au From: Paul King Date: Wed, 6 Feb 2019 11:38:55 +1000 Message-ID: Subject: Re: AST Transformation Issues To: nikolai.dueck@googlemail.com Cc: users@groovy.apache.org Content-Type: multipart/alternative; boundary="000000000000b222b805812fca2c" --000000000000b222b805812fca2c Content-Type: text/plain; charset="UTF-8" Hi, With regard to stack overflow when printing. This is a known limitation. ToString has been made smart enough to handle self references, e.g. import groovy.transform.* @ToString class Tree { Tree left, right } def t1 = new Tree() t1.left = t1 println t1 // => Tree((this), null) but isn't smart enough to handle mutual recursion, e.g.: def t2 = new Tree() t1.left = t2 t2.left = t1 println t1 // => StackOverflowError The plan has always been to make it smarter but we haven't done it yet. PRs welcome. If anyone is interested, I'd recommend something simple like what we have done for lists and maps in the respective InvokerHelper methods. E.g. for maps, self reference is already handled: def t3 = [:] t3.with{ left = t3; right = t3 } println t3 // => [left:(this Map), right:(this Map)] And mutual reference is handled by setting a maximum size (30 in the example below and three dots is used once the toString becomes greater than 30 in size): import org.codehaus.groovy.runtime.InvokerHelper def t4 = [left: t3] t3.right = t4 println InvokerHelper.toMapString(t3, 30) // => [left:(this Map), right:[left:[...]]] It works so long as the Map contents themselves don't have stack overflow scenarios that aren't catered for (some scenarios are handled). Similarly for lists, self reference is handled: def items = [] 3.times{ items << items } println items // [(this Collection), (this Collection), (this Collection)] but you can limit the size (e.g. for the case above) or to handle mutual reference: println InvokerHelper.toListString(items, 30) // => [(this Collection), (this Collection), ...] def list1 = [] def list2 = [] list1 << list2 list2 << list1 //println list1 // StackOverflowError println InvokerHelper.toListString(list1, 10) // => [[[[[[[[[[[...]]]]]]]]]]] So getting back to @ToString, I'd imagine an enhancement could involve either generating a toString(int maxSize) method or supporting a maxSize annotation attribute that was automatically supported in the generated code for the normal toString method. With regard to the AutoClone issues, as per the documentation, the supported cloning styles have various assumptions, e.g. SIMPLE style assumes child classes are of a similar style (or have equivalent methods), and the basic styles support only shallow cloning. For your case you want deep cloning, so SERIALIZATION would be the way to go. abstract class AbstractEvent { Date created String createdBy Date modified String modifiedBy } @AutoClone(style=SERIALIZATION) @ToString(includeSuper = true) class Event extends AbstractEvent implements Serializable { Long id String someText ArrayList subEvents = new ArrayList() } @AutoClone(style=CLONE) @ToString(includeSuper = true, excludes = ['event']) class SubEvent extends AbstractEvent implements Serializable { Long id String someText Event event } Cheers, Paul. Virus-free. www.avast.com <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> On Wed, Feb 6, 2019 at 7:46 AM Nikolai (gmail) wrote: > Hello, > > I experienced some Issues using ToString and AutoClone on Entities (Spring > JPA). > > Both can lead to endless loops / a stackoverflow, when classes reference > each other. > > While it's fine for AutoClone, I think the generated toString-Method > should recognize it was already called once and return a dummy value or > just the object reference. > > > Anyway, I got bigger issues using AutoClone: > > 1. I use an abstract class to add auditing columns to all my tables. All > my Entities inherit from that class (AbstractEvent in this example). Since > these fields should be empty/null on cloning, I'd like to omit the > AutoClone annotation on that class. But this leads to an error: No > signature of method: Event.cloneOrCopyMembers() is applicable for argument > types: (Event) values: [Event(null, null, [], Event@54d9d12d)]. > > 2. SubEvent objects within Event.subEvents were not cloned. > > import groovy.transform.AutoClone > import groovy.transform.Canonical > import groovy.transform.ToString > import static groovy.transform.AutoCloneStyle.SIMPLE > > @AutoClone(style=SIMPLE) //1. error when you remove this > abstract class AbstractEvent { > > Date created > String createdBy > > Date modified > String modifiedBy > } > > @AutoClone(style=SIMPLE) > @ToString(includeSuper = true) > class Event extends AbstractEvent { > Long id > String someText > > ArrayList subEvents = new ArrayList(); > } > > @AutoClone(style=SIMPLE) > @ToString(includeSuper = true, excludes = ['event']) > class SubEvent extends AbstractEvent { > Long id > String someText > > Event event; > } > > public static void main(String[] args){ > Event event = new Event( > id: 1, > someText: "Event 1", > created: new Date(), > createdBy: "me"); > > SubEvent subEvent1 = new SubEvent( > id: 1, > someText: "SubEvent 1", > event: event); > > event.subEvents += subEvent1 > > > Event clonedEvent = event.clone() > assert !event.is(clonedEvent) > assert !event.subEvents.is(clonedEvent.subEvents) > assert !event.subEvents.first().is(clonedEvent.subEvents.first()) // > 2. fails > } > > > Hope, you can help me here. > > > > Kind Regards > > Nikolai > --000000000000b222b805812fca2c Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable

Hi,

= With regard to stack overflow when printing. This is a known limitation. To= String has been made smart enough to handle self references, e.g.

=C2=A0 =C2=A0 import groovy.transform.*

=
=C2=A0 =C2=A0 @ToString
=C2=A0 =C2=A0 class Tree {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Tree left, right
=C2=A0 =C2=A0= }

=C2=A0 =C2=A0 def t1 =3D new Tree()
= =C2=A0 =C2=A0 t1.left =3D t1
=C2=A0 =C2=A0 println t1 // =3D>= =C2=A0Tree((this), null)

but isn't smart= enough to handle mutual recursion, e.g.:

=C2= =A0 =C2=A0 def t2 =3D new Tree()
=C2=A0 =C2=A0 t1.left =3D t2
=C2=A0 =C2=A0 t2.left =3D t1
=C2=A0 =C2=A0 println t1 // = =3D> StackOverflowError

The plan has alwa= ys been to make it smarter but we haven't done it yet. PRs welcome.

If anyone is interested, I'd recommend something = simple like what we have done for lists and maps in the respective InvokerH= elper methods.
E.g. for maps, self reference is already handled:<= /div>

=C2=A0 =C2=A0 def t3 =3D [:]
=C2=A0= =C2=A0 t3.with{ left =3D t3; right =3D t3 }
=C2=A0 =C2=A0 printl= n t3 // =3D> [left:(this Map), right:(this Map)]

And mutual reference is handled by setting a maximum size (30 in the exam= ple below and three dots is used once the toString becomes greater than 30 = in size):

=C2=A0 =C2=A0=C2=A0import org.codehaus.g= roovy.runtime.InvokerHelper
=C2=A0 =C2=A0 def t4 =3D [left: t3]
=C2=A0 =C2=A0 t3.right =3D t4
=C2=A0 =C2=A0 println Invo= kerHelper.toMapString(t3, 30) // =3D> [left:(this Map), right:[left:[...= ]]]

It works so long as the Map contents the= mselves don't have stack overflow scenarios that aren't catered for= (some scenarios are handled).

Similarly for lists= , self reference is handled:

=C2=A0 =C2=A0 de= f items =3D []
=C2=A0 =C2=A0 3.times{ items << items }
=C2=A0 =C2=A0 println items // [(this Collection), (this Collection),= (this Collection)]

but you can limit the si= ze (e.g. for the case above) or to handle mutual reference:

<= /div>
=C2=A0 =C2=A0 println InvokerHelper.toListString(items, 30) = // =3D> [(this Collection), (this Collection), ...]

=
=C2=A0 =C2=A0 def list1 =3D []
=C2=A0 =C2=A0 def list2 =3D [= ]
=C2=A0 =C2=A0 list1 << list2
=C2=A0 =C2=A0 list= 2 << list1
=C2=A0 =C2=A0 //println list1 // StackOverflowEr= ror
=C2=A0 =C2=A0 println InvokerHelper.toListString(list1, 10) /= / =3D> [[[[[[[[[[[...]]]]]]]]]]]

So getti= ng back to=C2=A0@ToString, I'd imagine an enhancement could involve eit= her generating a toString(int maxSize) method or supporting a maxSize annot= ation attribute that was automatically supported in the generated code for = the normal toString method.

With regard to the Aut= oClone issues, as per the documentation, the supported cloning styles have = various assumptions, e.g. SIMPLE style assumes child classes are of a simil= ar style (or have equivalent methods), and the basic styles support only sh= allow cloning. For your case you want deep cloning, so SERIALIZATION would = be the way to go.

=C2=A0 =C2=A0 abstract clas= s AbstractEvent {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Date created
<= div>=C2=A0 =C2=A0 =C2=A0 =C2=A0 String createdBy
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 Date modified
=C2=A0 =C2=A0 =C2=A0 =C2=A0 String mo= difiedBy
=C2=A0 =C2=A0 }

=C2= =A0 =C2=A0 @AutoClone(style=3DSERIALIZATION)
=C2=A0 =C2=A0 @ToStr= ing(includeSuper =3D true)
=C2=A0 =C2=A0 class Event extends Abst= ractEvent implements Serializable {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 L= ong id
=C2=A0 =C2=A0 =C2=A0 =C2=A0 String someText
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 ArrayList<SubEvent> subEvents =3D new ArrayL= ist()
=C2=A0 =C2=A0 }

=C2=A0 = =C2=A0 @AutoClone(style=3DCLONE)
=C2=A0 =C2=A0 @ToString(includeS= uper =3D true, excludes =3D ['event'])
=C2=A0 =C2=A0 clas= s SubEvent extends AbstractEvent implements Serializable {
=C2=A0= =C2=A0 =C2=A0 =C2=A0 Long id
=C2=A0 =C2=A0 =C2=A0 =C2=A0 String = someText
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Event event
=C2=A0= =C2=A0 }

Cheers, Paul.

=



=

3D"" Virus-free. www.avast.com

On Wed, Feb 6, 2019 at 7:46 AM Nikolai (gmail) <nikolai.dueck@googlemail.com> wr= ote:
=20 =20 =20

Hello,

I experienced some Issues using ToString and AutoClone on Entities (Spring JPA).

Both can lead to endless loops / a stackoverflow, when classes reference each other.

While it's fine for AutoClone, I think the generated toString-Method should recognize it was already called once and return a dummy value or just the object reference.


Anyway, I got bigger issues using AutoClone:

1. I use an abstract class to add auditing columns to all my tables. All my Entities inherit from that class (AbstractEvent in this example). Since these fields should be empty/null on cloning, I'd like to omit the AutoClone annotation on that class. But this leads to an error: No signature of method: Event.cloneOrCopyMembers() is applicable for argument types: (Event) values: [Event(null, null, [], Event@54d9d12d)].

2. SubEvent objects within Event.subEvents were not cloned.

import groovy.transform.AutoClone
import groovy.transform.Canonical
import groovy.transform.ToString
import static groovy.transform.AutoCloneStyle.SIMPLE

@AutoClone(style=3DSIMPLE) //1. error when you remove this
abstract class AbstractEvent {

=C2=A0=C2=A0=C2=A0 Date created
=C2=A0=C2=A0=C2=A0 String createdBy

=C2=A0=C2=A0=C2=A0 Date modified
=C2=A0=C2=A0=C2=A0 String modifiedBy
}

@AutoClone(style=3DSIMPLE)
@ToString(includeSuper =3D true)
class Event extends AbstractEvent {
=C2=A0=C2=A0=C2=A0 Long id
=C2=A0=C2=A0=C2=A0 String someText

=C2=A0=C2=A0=C2=A0 ArrayList<SubEvent> subEvents =3D new Ar= rayList();
}

@AutoClone(style=3DSIMPLE)
@ToString(includeSuper =3D true, excludes =3D ['event'])<= br> class SubEvent extends AbstractEvent {
=C2=A0=C2=A0=C2=A0 Long id
=C2=A0=C2=A0=C2=A0 String someText

=C2=A0=C2=A0=C2=A0 Event event;
}

public static void main(String[] args){
=C2=A0=C2=A0=C2=A0 Event event =3D new Event(
=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 id: 1,
=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 someText: "Event 1&quo= t;,
=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 created: new Date(),
=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 createdBy: "me");=

=C2=A0=C2=A0=C2=A0 SubEvent subEvent1 =3D new SubEvent(
=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 id: 1,
=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 someText: "SubEvent 1&= quot;,
=C2=A0=C2=A0=C2=A0 =C2=A0=C2=A0=C2=A0 event: event);

=C2=A0=C2=A0=C2=A0 event.subEvents +=3D subEvent1


=C2=A0=C2=A0=C2=A0 Event clonedEvent =3D event.clone()
=C2=A0=C2=A0=C2=A0 assert !event.is(clonedEvent)
=C2=A0=C2=A0=C2=A0 assert !event.subEvents.is(clonedEvent.subEvents)
=C2=A0=C2=A0=C2=A0 assert !event.subEvents.first().is(clonedEvent.subEvents.first()) // 2. fails
}


Hope, you can help me here.



Kind Regards

Nikolai

--000000000000b222b805812fca2c--