Henry, thank you, it is quite possible that you are right, at least I should check it out. Thank you again.
среда, 18 мая 2022 г. в 07:39:49 UTC+3, Henry: > Kudos to you. Java to Go is a lot of work. Java is a more complex > language. It has more features and more possibilities for expressing the > same model/algorithm. You need to identify all these possibilities, parse, > and convert them to Go. I could be wrong about this, but it may be easier > to convert Java bytecode to Go. > > On Tuesday, May 17, 2022 at 9:49:09 PM UTC+7 alex-coder wrote: > >> In Go, multithreading is one of the main features, respectively, the >> ability to convert Java code using multithreading into Go code >> automatically, which uses channels, is very important. >> I found a suitable example of ProducerConsumerExample from Oracle >> there:Oracle >> sample >> <https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html>, >> >> where the Drop class is a candidate for conversion to Go using channels. >> But, now I give an intermediate result without channels. >> Also, I read the opinion that the conversion of Java code where >> annotations are used in runtime is impossible. It is necessary to >> investigate the problem and try to understand if it is possible to solve it >> somehow. Before transferring a project from the status of "possibility of >> achievement" to the status of "prototype", it is necessary to see what can >> be done with these problems in relation to annotations. >> *Java code:* >> package com.builder.start.here; >> >> import java.util.Random; >> >> public class RunMeDrop { >> >> public static void main(String[] args) { >> Drop drop = new Drop(); >> (new Thread(new Producer(drop))).start(); >> (new Thread(new Consumer(drop))).start(); >> } >> } >> class Drop { >> // Message sent from producer >> // to consumer. >> private String message; >> // True if consumer should wait >> // for producer to send message, >> // false if producer should wait for >> // consumer to retrieve message. >> private boolean empty = true; >> >> public synchronized String take() { >> // Wait until message is >> // available. >> while (empty) { >> try { >> wait(); >> } catch (InterruptedException e) {} >> } >> // Toggle status. >> empty = true; >> // Notify producer that >> // status has changed. >> notifyAll(); >> return message; >> } >> >> public synchronized void put(String message) { >> // Wait until message has >> // been retrieved. >> while (!empty) { >> try { >> wait(); >> } catch (InterruptedException e) {} >> } >> // Toggle status. >> empty = false; >> // Store message. >> this.message = message; >> // Notify consumer that status >> // has changed. >> notifyAll(); >> } >> } >> class Consumer implements Runnable { >> private Drop drop; >> >> public Consumer(Drop drop) { >> this.drop = drop; >> } >> >> public void run() { >> Random random = new Random(); >> for ( >> String message = drop.take(); >> ! message.equals("DONE"); >> message = drop.take()) { >> System.out.format( >> "MESSAGE RECEIVED: %s%n", message); >> try { >> Thread.sleep(random.nextInt(5000)); >> } catch (InterruptedException e) {} >> } >> } >> } >> class Producer implements Runnable { >> private Drop drop; >> >> public Producer(Drop drop) { >> this.drop = drop; >> } >> >> public void run() { >> String importantInfo[] = { >> "Mares eat oats", >> "Does eat oats", >> "Little lambs eat ivy", >> "A kid will eat ivy too" >> }; >> Random random = new Random(); >> >> for (int i = 0; i < importantInfo.length; i++) { >> drop.put(importantInfo[i]); >> try { >> Thread.sleep(random.nextInt(5000)); >> } catch (InterruptedException e) {} >> } >> drop.put("DONE"); >> >> } >> } >> >> *Converter gave out:* >> package main >> >> import ( >> "fmt" >> "math/rand" >> "os" >> "sync" >> "time" >> ) >> >> type RunMe struct{} >> >> func NewRunMe() *RunMe { >> var runMe RunMe = RunMe{} >> return &runMe >> >> } >> func main() { >> >> var args []string = os.Args >> var rm *RunMe = NewRunMe() >> rm.RunMe_main(&args) >> } >> >> /** generated method **/ >> func (runMe *RunMe) RunMe_main(args *[]string) { >> wg := &sync.WaitGroup{} >> var drop *Drop = NewDrop() >> wg.Add(1) >> go (&Thread{NewProducer(drop)}).run(wg) >> wg.Add(1) >> go (&Thread{NewConsumer(drop)}).run(wg) >> wg.Wait() >> } >> >> type Drop struct { >> /** generated field */ >> cond *sync.Cond >> message *string >> empty *bool >> } >> >> func NewDrop() *Drop { >> >> var drop Drop = Drop{} >> drop.cond = sync.NewCond(&sync.Mutex{}) >> empty := true >> drop.empty = &empty >> return &drop >> } >> func (drop *Drop) take() string { >> drop.cond.L.Lock() >> for *drop.empty { >> drop.cond.Wait() >> } >> >> *drop.empty = true >> drop.cond.L.Unlock() >> drop.cond.Broadcast() >> return *drop.message >> } >> func (drop *Drop) put(message *string) { >> drop.cond.L.Lock() >> for !*drop.empty { >> drop.cond.Wait() >> } >> >> *drop.empty = false >> drop.message = message >> drop.cond.L.Unlock() >> drop.cond.Broadcast() >> } >> >> type Consumer struct { >> drop *Drop >> } >> >> func NewConsumer(drop *Drop) *Consumer { >> var consumer Consumer = Consumer{drop} >> return &consumer >> } >> func (consumer *Consumer) run(wg *sync.WaitGroup) { >> defer wg.Done() >> >> var random *Random = NewRandom() >> for message := consumer.drop.take(); >> message != "DONE"; >> message = consumer.drop.take() { >> fmt.Printf("MESSAGE RECEIVED: %s\n", message) >> time.Sleep(random.nextInt(5000)) >> } >> } >> >> type Producer struct { >> drop *Drop >> } >> >> func NewProducer(drop *Drop) *Producer { >> var producer Producer = Producer{drop} >> return &producer >> } >> func (producer *Producer) run(wg *sync.WaitGroup) { >> defer wg.Done() >> >> var importantInfo [4]string = [4]string{ >> "Mares eat oats", >> "Does eat oats", >> "Little lambs eat ivy", >> "A kid will eat ivy too", >> } >> >> var random *Random = NewRandom() >> for i := 0; i < len(importantInfo); i++ { >> producer.drop.put(&importantInfo[i]) >> time.Sleep(random.nextInt(5000)) >> } >> s := "DONE" >> producer.drop.put(&s) >> } >> >> type Random struct{} >> >> func NewRandom() *Random { >> var random Random = Random{} >> return &random >> } >> >> func (r *Random) nextInt(n int) time.Duration { >> return time.Duration(rand.Intn(n)) >> } >> >> type Thread struct { >> Runnable >> } >> >> func (t *Thread) run(wg *sync.WaitGroup) { >> t.Runnable.run(wg) >> } >> >> type Runnable interface { >> run(wg *sync.WaitGroup) >> } >> The Go Playground, you can run code there: >> Go Playground. <https://go.dev/play/p/o7tXt8p2m_3> >> >> Thank you to all very much. >> >> четверг, 28 апреля 2022 г. в 19:08:51 UTC+3, alex-coder: >> >>> So, multithreading. >>> I found a simple example from Oracle with the wonderful name "Bad >>> Threads" there: >>> >>> https://docs.oracle.com/javase/tutorial/essential/concurrency/QandE/questions.html >>> >>> , simplified it even more, called the example a new simple name One and >>> extend a Converter to translate the code to Golang. >>> There is no similar processing for InterruptedException in go, so the >>> Converter does not translate the processing of this interrupt in any way. >>> If anyone has a relatively small example in Java, please share it. >>> *Java code:* >>> package run.me; >>> public class One { >>> String msg; >>> public static void main( String[] args) throws InterruptedException { >>> One one=new One(); >>> one.msg="Initial message"; >>> System.out.println("main:before start:" + one.msg + " second part >>> of"); >>> new Second(one).start(); >>> one.msg="after go start"; >>> Thread.sleep(2000); >>> System.out.println("main:about to end:" + one.msg); >>> } >>> } >>> class Second extends Thread { >>> One one; >>> public Second( One one){ >>> this.one=one; >>> } >>> public void run(){ >>> try { >>> sleep(1000); >>> } catch ( InterruptedException e) { >>> } >>> System.out.println("run:after sleep:" + one.msg); >>> one.msg="try to change msg"; >>> } >>> } >>> *Converter выдал:* >>> package main >>> >>> import ( >>> "fmt" >>> "os" >>> "time" >>> ) >>> >>> type One struct { >>> msg *string >>> } >>> >>> func NewOne() *One { >>> var one One = One{} >>> return &one >>> } >>> func main() { >>> var args []string = os.Args >>> var o_dummy *One = NewOne() >>> o_dummy.One_main(&args) >>> } >>> >>> /** generated method **/ >>> func (one_one *One) One_main(args *[]string) { >>> var one *One = NewOne() >>> s := "Initial message" >>> one.msg = &s >>> fmt.Println("main:before start:" + *one.msg + " second part of") >>> go NewSecond(one).run() >>> s_s := "after go start" >>> one.msg = &s_s >>> time.Sleep(2000) >>> fmt.Println("main:about to end:" + *one.msg) >>> } >>> >>> type Second struct { >>> one *One >>> } >>> >>> func NewSecond(one *One) *Second { >>> var second Second = Second{one} >>> return &second >>> } >>> func (second *Second) run() { >>> time.Sleep(1000) >>> fmt.Println("run:after sleep:" + *second.one.msg) >>> s_s_s := "try to change msg" >>> second.one.msg = &s_s_s >>> } >>> >>> The next example will probably be about channel's. >>> If anyone has an interesting example, write me, I'll think about how to >>> solve it. I just can't promise that it will be quickly. :-) >>> >>> Thank you to all ! >>> >>> четверг, 7 апреля 2022 г. в 18:52:25 UTC+3, alex-coder: >>> >>>> Thanks for the comments about generating code to handle exceptions. . >>>> Here it is a new version. >>>> package main >>>> >>>> import ( >>>> "fmt" >>>> "os" >>>> ) >>>> >>>> type CatchException struct{} >>>> >>>> func main() { >>>> >>>> var args []string = os.Args >>>> >>>> var ce CatchException = CatchException{} >>>> ce.CatchException_main(args) >>>> } >>>> >>>> /** generated method **/ >>>> func (catchException *CatchException) CatchException_main(args >>>> []string) { >>>> defer func() { >>>> if err := recover(); err != nil { >>>> exc := err.(Exception) >>>> switch exc.msg { >>>> >>>> case "Exception": >>>> fmt.Println("yes, I caught it") >>>> default: >>>> fmt.Println("No, something is not right") >>>> } >>>> } >>>> fmt.Println("finally processing") >>>> }() >>>> >>>> (&ThrowException{}).runme() >>>> } >>>> >>>> type ThrowException struct{} >>>> >>>> func (throwException *ThrowException) runme() { >>>> panic(Exception{"Exception"}) >>>> } >>>> >>>> type Exception struct { >>>> msg string >>>> } >>>> >>>> понедельник, 4 апреля 2022 г. в 14:12:37 UTC+3, alex-coder: >>>> >>>>> >>>>> >>>>> >>>>> >>>>> *Another use case for automatically translating codewritten in Java to >>>>> Golang is Exception Handling.The next example is likely to be about >>>>> multithreading.Example in Java:* >>>>> package com.builder.start.here; >>>>> >>>>> public class CatchException { >>>>> >>>>> public static void main(String[] args) { >>>>> try { >>>>> new ThrowException().runme(); >>>>> } catch (Exception e) { >>>>> System.out.println("yes, I caught it"); >>>>> } finally { >>>>> System.out.println("finally processing"); >>>>> } >>>>> >>>>> } >>>>> >>>>> } >>>>> >>>>> class ThrowException{ >>>>> public void runme() throws Exception{ >>>>> throw new Exception(); >>>>> } >>>>> } >>>>> >>>>> *Converter gave out:* >>>>> >>>>> package main >>>>> >>>>> import ( >>>>> "fmt" >>>>> "os" >>>>> ) >>>>> >>>>> type CatchException struct{} >>>>> >>>>> >>>>> func main() { >>>>> >>>>> var args []string = os.Args >>>>> >>>>> var ce CatchException = CatchException{} >>>>> ce.CatchException_main(args) >>>>> } >>>>> >>>>> /** generated method **/ >>>>> func (catchException *CatchException) CatchException_main(args >>>>> []string) { >>>>> defer func() { >>>>> if err := recover(); err != nil { >>>>> str := err.(string) >>>>> switch str { >>>>> case "Exception": >>>>> fmt.Println("yes, I caught it") >>>>> default: >>>>> fmt.Println("No, something is not right") >>>>> } >>>>> } >>>>> fmt.Println("finally processing") >>>>> }() >>>>> >>>>> (&ThrowException{}).runme() >>>>> } >>>>> >>>>> type ThrowException struct{} >>>>> >>>>> func (throwException *ThrowException) runme() { >>>>> panic("Exception") >>>>> } >>>>> воскресенье, 27 марта 2022 г. в 15:11:48 UTC+3, alex-coder: >>>>> >>>>>> After several months of switching from Java to Golang, it seemed to >>>>>> me that >>>>>> it would be interesting to make the translation of Java code into >>>>>> Golang automatically. >>>>>> The text below shows what has been done so far. >>>>>> >>>>>> The work is not a prototype, but rather indicates the possibility of >>>>>> achieving a result. >>>>>> Therefore, I deliberately simplify the development context of the >>>>>> Converter where it was >>>>>> possible. >>>>>> >>>>>> At first it seemed important to me that between Java and Go there is >>>>>> a difference >>>>>> between the implementation of the Dynamic Dispatching, more >>>>>> precisely, there is no >>>>>> Dynamic Dispatching in Go. The applied solution in the current >>>>>> implementation >>>>>> looks not only ugly but even violates the several very important >>>>>> rules of the OO design, >>>>>> I'm not kidding here. But this option looks like that it will be >>>>>> working. >>>>>> >>>>>> Onward I will provide the 4 code samples in Java, followed by the >>>>>> automatically >>>>>> generated Golang code and comments as needed. >>>>>> >>>>>> *1. Of course, I started with the most popular program: "Hello >>>>>> World".* >>>>>> >>>>>> package main; >>>>>> >>>>>> public class HelloWorld { >>>>>> public static void main( String[] args){ >>>>>> System.out.println("Hello World"); >>>>>> } >>>>>> } >>>>>> >>>>>> *Converter gave out:* >>>>>> >>>>>> package main >>>>>> >>>>>> import ( >>>>>> "fmt" >>>>>> "os" >>>>>> ) >>>>>> >>>>>> type HelloWorld struct{} >>>>>> >>>>>> func main() { >>>>>> >>>>>> var args []string = os.Args >>>>>> >>>>>> var hw HelloWorld = HelloWorld{} >>>>>> hw.HelloWorld_main(args) >>>>>> } >>>>>> >>>>>> /** generated method **/ >>>>>> func (helloWorld *HelloWorld) HelloWorld_main(args []string) { >>>>>> fmt.Println("Hello World") >>>>>> } >>>>>> >>>>>> *2. Next, it was interesting to deal with the problem of a simple >>>>>> inheritance.* >>>>>> >>>>>> package main; >>>>>> >>>>>> public class TestInheritance { >>>>>> public static void main( String[] args){ >>>>>> Inheritance inh=null; >>>>>> inh=new Second(); >>>>>> inh.hello(); >>>>>> inh=new Third(); >>>>>> inh.hello(); >>>>>> } >>>>>> } >>>>>> public interface Inheritance { >>>>>> public void hello(); >>>>>> } >>>>>> class Second implements Inheritance { >>>>>> public void hello(){ >>>>>> System.out.println("Second"); >>>>>> } >>>>>> } >>>>>> class Third implements Inheritance { >>>>>> public void hello(){ >>>>>> System.out.println("Third"); >>>>>> } >>>>>> } >>>>>> >>>>>> *Converter gave out:* >>>>>> >>>>>> package main >>>>>> >>>>>> import ( >>>>>> "fmt" >>>>>> "os" >>>>>> ) >>>>>> >>>>>> type TestInheritance struct{} >>>>>> >>>>>> func main() { >>>>>> >>>>>> var args []string = os.Args >>>>>> >>>>>> var ti TestInheritance = TestInheritance{} >>>>>> ti.TestInheritance_main(args) >>>>>> } >>>>>> >>>>>> /** generated method **/ >>>>>> func (testInheritance *TestInheritance) TestInheritance_main(args >>>>>> []string) { >>>>>> >>>>>> var inh Inheritance >>>>>> inh = AddressSecond(Second{}) >>>>>> inh.hello() >>>>>> inh = AddressThird(Third{}) >>>>>> inh.hello() >>>>>> } >>>>>> >>>>>> type Inheritance interface { >>>>>> hello() >>>>>> } >>>>>> type Second struct{} >>>>>> >>>>>> func (second *Second) hello() { >>>>>> fmt.Println("Second") >>>>>> } >>>>>> >>>>>> type Third struct{} >>>>>> >>>>>> func (third *Third) hello() { >>>>>> fmt.Println("Third") >>>>>> } >>>>>> >>>>>> func AddressSecond(s Second) *Second { return &s } >>>>>> func AddressThird(t Third) *Third { return &t } >>>>>> >>>>>> >>>>>> *3. In the following example, it is necessary to correctly define >>>>>> a common interface for the inheritance tree.* >>>>>> >>>>>> package no.packeges; >>>>>> >>>>>> public class TestExtension { >>>>>> public static void main( String[] args){ >>>>>> TestExtension te=new TestExtension(); >>>>>> te.hello(); >>>>>> te=new Second(); >>>>>> te.hello(); >>>>>> te=new Third(); >>>>>> te.hello(); >>>>>> te=new Fourth(); >>>>>> te.hello(); >>>>>> } >>>>>> public void hello(){ >>>>>> System.out.println("hello"); >>>>>> } >>>>>> } >>>>>> class Second extends TestExtension { >>>>>> public void hello(){ >>>>>> System.out.println("Second"); >>>>>> } >>>>>> } >>>>>> class Third extends TestExtension { >>>>>> public void hello(){ >>>>>> System.out.println("Third"); >>>>>> } >>>>>> } >>>>>> class Fourth extends Third { >>>>>> public void hello(){ >>>>>> System.out.println("Fourth"); >>>>>> } >>>>>> } >>>>>> >>>>>> *Converter gave out:* >>>>>> >>>>>> package main >>>>>> >>>>>> import ( >>>>>> "fmt" >>>>>> "os" >>>>>> ) >>>>>> >>>>>> type TestExtension struct{} >>>>>> >>>>>> func main() { >>>>>> >>>>>> var args []string = os.Args >>>>>> >>>>>> var te TestExtension = TestExtension{} >>>>>> te.TestExtension_main(args) >>>>>> } >>>>>> func (testExtension *TestExtension) hello() { >>>>>> fmt.Println("hello") >>>>>> } >>>>>> >>>>>> /** generated method **/ >>>>>> func (testExtension *TestExtension) TestExtension_main(args []string) >>>>>> { >>>>>> >>>>>> var te ITestExtension = AddressTestExtension(TestExtension{}) >>>>>> te.hello() >>>>>> te = AddressSecond(Second{}) >>>>>> te.hello() >>>>>> te = AddressThird(Third{}) >>>>>> te.hello() >>>>>> te = AddressFourth(Fourth{}) >>>>>> te.hello() >>>>>> } >>>>>> >>>>>> type Second struct { >>>>>> TestExtension >>>>>> } >>>>>> >>>>>> func (second *Second) hello() { >>>>>> fmt.Println("Second") >>>>>> } >>>>>> >>>>>> type Third struct { >>>>>> TestExtension >>>>>> } >>>>>> >>>>>> func (third *Third) hello() { >>>>>> fmt.Println("Third") >>>>>> } >>>>>> >>>>>> type Fourth struct { >>>>>> Third >>>>>> } >>>>>> >>>>>> func (fourth *Fourth) hello() { >>>>>> fmt.Println("Fourth") >>>>>> } >>>>>> >>>>>> type ITestExtension interface { >>>>>> /** Generated Method */ >>>>>> hello() >>>>>> } >>>>>> >>>>>> func AddressSecond(s Second) *Second { return &s >>>>>> } >>>>>> func AddressThird(t Third) *Third { return &t >>>>>> } >>>>>> func AddressTestExtension(t TestExtension) *TestExtension { return &t >>>>>> } >>>>>> func AddressFourth(f Fourth) *Fourth { return &f >>>>>> } >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> *4. Now the Dynamic Dispatching absence. The problematic method in >>>>>> the last sample is the amount() in class Repeater. Without rewriting >>>>>> the code, I think it would be impossible to invoke it correctly.* >>>>>> >>>>>> package main; >>>>>> >>>>>> public class Speaker { >>>>>> String message; >>>>>> public static void main( String[] args){ >>>>>> Speaker sp = new Speaker("Say hello !"); >>>>>> System.out.println(sp.amount()); >>>>>> sp.speak(); >>>>>> Repeater rp = new Repeater("Say hello !",3); >>>>>> System.out.println(rp.amount()); >>>>>> rp.speak(); >>>>>> } >>>>>> public Speaker( String message){ >>>>>> this.message=message; >>>>>> } >>>>>> public void speak(){ >>>>>> for (int i=0; i < amount(); i++) { >>>>>> System.out.println(this.message); >>>>>> } >>>>>> } >>>>>> public int amount(){ >>>>>> return 1; >>>>>> } >>>>>> } >>>>>> class Repeater extends Speaker { >>>>>> int to_repeat=0; >>>>>> public Repeater( String message, int amount){ >>>>>> super(message); >>>>>> this.to_repeat=amount; >>>>>> } >>>>>> public int amount(){ >>>>>> return this.to_repeat; >>>>>> } >>>>>> } >>>>>> >>>>>> *Converter gave out:* >>>>>> >>>>>> package main >>>>>> >>>>>> import ( >>>>>> "fmt" >>>>>> "os" >>>>>> ) >>>>>> >>>>>> type Speaker struct { >>>>>> message string >>>>>> } >>>>>> >>>>>> func main() { >>>>>> >>>>>> var args []string = os.Args >>>>>> >>>>>> var s_dummy Speaker = NewSpeaker("") >>>>>> s_dummy.Speaker_main(args) >>>>>> } >>>>>> func NewSpeaker(message string) Speaker { >>>>>> >>>>>> var speaker Speaker = Speaker{message} >>>>>> return speaker >>>>>> } >>>>>> func (speaker *Speaker) speak() { >>>>>> for i := 0; i < speaker.amount(); i++ { >>>>>> fmt.Println(speaker.message) >>>>>> } >>>>>> } >>>>>> func (speaker *Speaker) amount() int { >>>>>> return 1 >>>>>> } >>>>>> >>>>>> /** generated method **/ >>>>>> func (speaker *Speaker) Speaker_main(args []string) { >>>>>> >>>>>> var sp ISpeaker = AddressSpeaker(NewSpeaker("Say hello !")) >>>>>> fmt.Println(sp.amount()) >>>>>> sp.speak() >>>>>> >>>>>> var rp ISpeaker = AddressRepeater(NewRepeater("Say hello !", >>>>>> 3)) >>>>>> fmt.Println(rp.amount()) >>>>>> rp.speak() >>>>>> } >>>>>> >>>>>> type Repeater struct { >>>>>> Speaker >>>>>> >>>>>> to_repeat int >>>>>> } >>>>>> >>>>>> func NewRepeater(message string, amount int) Repeater { >>>>>> >>>>>> var repeater Repeater = Repeater{NewSpeaker(message), amount} >>>>>> return repeater >>>>>> } >>>>>> func (repeater *Repeater) amount() int { >>>>>> return repeater.to_repeat >>>>>> } >>>>>> func (repeater *Repeater) speak() { >>>>>> for i := 0; i < repeater.amount(); i++ { >>>>>> fmt.Println(repeater.message) >>>>>> } >>>>>> } >>>>>> >>>>>> type ISpeaker interface { >>>>>> >>>>>> /** Generated Method */ >>>>>> >>>>>> amount() int >>>>>> /** Generated Method */ >>>>>> >>>>>> speak() >>>>>> } >>>>>> >>>>>> func AddressRepeater(r Repeater) *Repeater { return &r } >>>>>> func AddressSpeaker(s Speaker) *Speaker { return &s } >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> *5. I will be thankful for any feedback. Thanks to everyone.* >>>>>> >>>>> -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/feed57bf-ec6c-4b7d-b830-8cec5016429cn%40googlegroups.com.