Re: Problem decoding complex AutoBean

2020-07-10 Thread Colin Alworth
I'm not seeing any issue with that code and sample JSON. Here's a quick 
entrypoint that I made:

@Override
public void onModuleLoad() {
IWebchatThemeConfigurationBean bean = deserializeFromJson("{\n" +
"\"themeId\": 1,\n" +
"\"name\": \"rpc\",\n" +
"\"enabled\": true,\n" +
"\"propertiesList\": [\n" +
"{\n" +
"\"id\": 1,\n" +
"\"themeConfigurationId\": 1,\n" +
"\"chatScreen\": \"chatScreen\",\n" +
"\"component\": \"header\",\n" +
"\"property\": \"text\",\n" +
"\"value\": \"HELLO\"\n" +
"},\n" +
"{\n" +
"\"id\": 3,\n" +
"\"themeConfigurationId\": 1,\n" +
"\"chatScreen\": \"chatScreen\",\n" +
"\"component\": \"background\",\n" +
"\"property\": \"color\",\n" +
"\"value\": \"rgba(0,191,255,0.5)\"\n" +
"}\n" +
"]\n" +
"}");
DomGlobal.console.log(bean.getEnabled());
DomGlobal.console.log(bean.getThemeId() + "");
DomGlobal.console.log(bean.getName());
DomGlobal.console.log(String.valueOf(bean.getPropertiesList()));
DomGlobal.console.log(bean.getPropertiesList().size() + "");

DomGlobal.console.log(bean.getPropertiesList().stream().map(IWebchatThemeConfigurationPropertyBean::getValue).collect(Collectors.joining(",
 
")));
}


Notice that after running your parse method, it dumps the output to the JS 
console - here is the output:
true
1
rpc
[com.vertispan.draw.connected.client.FlowChartEntryPoint_IWebchatThemeConfigurationPropertyBeanAutoBean$2@1b,
 
com.vertispan.draw.connected.client.FlowChartEntryPoint_IWebchatThemeConfigurationPropertyBeanAutoBean$2@1c]
2
HELLO, rgba(0,191,255,0.5)

It seems to be working?

One note: if you do not actually call the properties() method, it is not 
needed in the factory, since the IWebchatThemeConfigurationPropertyBean 
type is already reachable from IWebchatThemeConfigurationBean.

Using GWT 2.8.2 for this test.

On Friday, July 10, 2020 at 3:23:35 PM UTC-5, Akshay Kumar wrote:
>
> Can anyone help me?
> My json string is - 
> {
> "themeId": 1,
> "name": "rpc",
> "enabled": true,
> "propertiesList": [
> {
> "id": 1,
> "themeConfigurationId": 1,
> "chatScreen": "chatScreen",
> "component": "header",
> "property": "text",
> "value": "HELLO"
> },
> {
> "id": 3,
> "themeConfigurationId": 1,
> "chatScreen": "chatScreen",
> "component": "background",
> "property": "color",
> "value": "rgba(0,191,255,0.5)"
> }
> ]
> }
>
> Interfaces are -
>
> public interface IWebchatThemeConfigurationBean { public Integer 
> getThemeId(); public String getName(); public Boolean getEnabled(); public 
> List getPropertiesList(); }
> public interface IWebchatThemeConfigurationPropertyBean { public Integer 
> getId(); public Integer getThemeConfigurationId(); public String 
> getChatScreen(); public String getComponent(); public String getProperty(); 
> public String getValue(); }
>
> public interface ChatFactory extends AutoBeanFactory {
> AutoBean theme();
>
> AutoBean properties();
> }
> ChatFactory factory;
> factory = GWT.create(ChatFactory.class);
>
> IWebchatThemeConfigurationBean deserializeFromJson(String json) { 
> AutoBean bean = 
> AutoBeanCodex.decode(factory, IWebchatThemeConfigurationBean.class, json); 
> return bean.as(); }
>
> in this returned bean I'm getting null propertiesList.
> name, enabled, themeId are fine.
>
> On Tuesday, August 20, 2013 at 8:07:30 AM UTC+5:30, Thad Humphries wrote:
>>
>> I've managed a simple AutoBean with a list. Now I've a more complex one, 
>> and I don't understand what's wrong. My get methods are returning null 
>> though the debugger shows the object has data.
>>
>> The JSON looks like this:
>>
>> {
>>   "mquery":
>> {
>>   "screenname":"Index Card",
>>   "fields":
>> {
>>   "field":[
>> {"title":"Name",  "name":"odname",   "value":"*TIFF*"},
>> {"title":"Date",  "name":"oddate",   "value":""},
>> {"title":"Ref #", "name":"odrefnum", "value":""}
>>   ]
>> }
>> }
>> } 
>>
>>
>> I have three interfaces:
>>
>> public interface Field {
>>   String getTitle();
>>   void setTitle(String title);
>>   String getName();
>>   void setName(String name);
>>   String getValue();
>>   void setValue(String value);
>> }
>>
>> public interface FieldList {
>>   List getField();
>>   void setField(List field);
>> }
>>
>> public interface MQuery {
>>   String getScreenname();
>>   void setScreenname(String screenname);
>>   FieldList getFields();
>>   void 

Re: Problem decoding complex AutoBean

2020-07-10 Thread Akshay Kumar
Can anyone help me?
My json string is - 
{
"themeId": 1,
"name": "rpc",
"enabled": true,
"propertiesList": [
{
"id": 1,
"themeConfigurationId": 1,
"chatScreen": "chatScreen",
"component": "header",
"property": "text",
"value": "HELLO"
},
{
"id": 3,
"themeConfigurationId": 1,
"chatScreen": "chatScreen",
"component": "background",
"property": "color",
"value": "rgba(0,191,255,0.5)"
}
]
}

Interfaces are -

public interface IWebchatThemeConfigurationBean { public Integer 
getThemeId(); public String getName(); public Boolean getEnabled(); public 
List getPropertiesList(); }
public interface IWebchatThemeConfigurationPropertyBean { public Integer 
getId(); public Integer getThemeConfigurationId(); public String 
getChatScreen(); public String getComponent(); public String getProperty(); 
public String getValue(); }

public interface ChatFactory extends AutoBeanFactory {
AutoBean theme();

AutoBean properties();
}
ChatFactory factory;
factory = GWT.create(ChatFactory.class);

IWebchatThemeConfigurationBean deserializeFromJson(String json) { 
AutoBean bean = 
AutoBeanCodex.decode(factory, IWebchatThemeConfigurationBean.class, json); 
return bean.as(); }

in this returned bean I'm getting null propertiesList.
name, enabled, themeId are fine.

On Tuesday, August 20, 2013 at 8:07:30 AM UTC+5:30, Thad Humphries wrote:
>
> I've managed a simple AutoBean with a list. Now I've a more complex one, 
> and I don't understand what's wrong. My get methods are returning null 
> though the debugger shows the object has data.
>
> The JSON looks like this:
>
> {
>   "mquery":
> {
>   "screenname":"Index Card",
>   "fields":
> {
>   "field":[
> {"title":"Name",  "name":"odname",   "value":"*TIFF*"},
> {"title":"Date",  "name":"oddate",   "value":""},
> {"title":"Ref #", "name":"odrefnum", "value":""}
>   ]
> }
> }
> } 
>
>
> I have three interfaces:
>
> public interface Field {
>   String getTitle();
>   void setTitle(String title);
>   String getName();
>   void setName(String name);
>   String getValue();
>   void setValue(String value);
> }
>
> public interface FieldList {
>   List getField();
>   void setField(List field);
> }
>
> public interface MQuery {
>   String getScreenname();
>   void setScreenname(String screenname);
>   FieldList getFields();
>   void setFields(FieldList fields);
> }
>
> In my bean factory, I have
>
> public interface BeanFactory extends AutoBeanFactory {
>   ...
>   AutoBean field();
>   AutoBean fields();
>   AutoBean mquery();
> }
>
> In my onResponseReceived() method, I call
>
>   AutoBean mqueryBean = 
>   AutoBeanCodex.decode(clientFactory.getBeanFactory(), 
>   MQuery.class, response.getText());
>   MQuery mQuery = mqueryBean.as();
>   logger.log(Level.INFO, mQuery.getScreenname());
>   screenName = mQuery.getScreenname();
>   fieldList = mQuery.getFields().getField();
>   view.renderFields(fieldList);
>
> When I look at the debugger in Chrome, screenname, etc are populated (in 
> mQuery:this$0:data_0 there is mquery with screenename set and 3 field 
> objects in fields). However mQuery.getScreenname() returns null as does 
> mQuery.getFields().
>
> What am I not seeing here? Could the presence of other AutoBean<>s in my 
> factory (beans that work) be messing me up (do I need a separate factory)?
>
>

-- 
You received this message because you are subscribed to the Google Groups "GWT 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/google-web-toolkit/9aaf50f4-7464-4498-8632-701e4fd217f5o%40googlegroups.com.


Re: Problem decoding complex AutoBean

2013-08-20 Thread Thomas Broyer
You don't seem to have an AutoBean interface with a MQuery getMquery() 
method.
The object you're parsing has only a mquery property, so if you look at 
it as if it were an MQuery object, getScreenname and getFields would 
expectedly be null.

On Tuesday, August 20, 2013 4:37:30 AM UTC+2, Thad Humphries wrote:

 I've managed a simple AutoBean with a list. Now I've a more complex one, 
 and I don't understand what's wrong. My get methods are returning null 
 though the debugger shows the object has data.

 The JSON looks like this:

 {
   mquery:
 {
   screenname:Index Card,
   fields:
 {
   field:[
 {title:Name,  name:odname,   value:*TIFF*},
 {title:Date,  name:oddate,   value:},
 {title:Ref #, name:odrefnum, value:}
   ]
 }
 }
 } 


 I have three interfaces:

 public interface Field {
   String getTitle();
   void setTitle(String title);
   String getName();
   void setName(String name);
   String getValue();
   void setValue(String value);
 }

 public interface FieldList {
   ListField getField();
   void setField(ListField field);
 }

 public interface MQuery {
   String getScreenname();
   void setScreenname(String screenname);
   FieldList getFields();
   void setFields(FieldList fields);
 }

 In my bean factory, I have

 public interface BeanFactory extends AutoBeanFactory {
   ...
   AutoBeanField field();
   AutoBeanFieldList fields();
   AutoBeanMQuery mquery();
 }

 In my onResponseReceived() method, I call

   AutoBeanMQuery mqueryBean = 
   AutoBeanCodex.decode(clientFactory.getBeanFactory(), 
   MQuery.class, response.getText());
   MQuery mQuery = mqueryBean.as();
   logger.log(Level.INFO, mQuery.getScreenname());
   screenName = mQuery.getScreenname();
   fieldList = mQuery.getFields().getField();
   view.renderFields(fieldList);

 When I look at the debugger in Chrome, screenname, etc are populated (in 
 mQuery:this$0:data_0 there is mquery with screenename set and 3 field 
 objects in fields). However mQuery.getScreenname() returns null as does 
 mQuery.getFields().

 What am I not seeing here? Could the presence of other AutoBeans in my 
 factory (beans that work) be messing me up (do I need a separate factory)?



-- 
You received this message because you are subscribed to the Google Groups 
Google Web Toolkit group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit+unsubscr...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.


Re: Problem decoding complex AutoBean

2013-08-20 Thread Thad Humphries
Thank you, Thomas. That worked.

I added the interface

public interface MQueryWrapper {
  MQuery getMquery();
}

In BeanFactory, I changed AutoBeanMQuery mquery(); for 
AutoBeanMQueryWrapper mquery();

My onResponseReceived() now looks like

  AutoBeanMQueryWrapper bean = 
  AutoBeanCodex.decode(clientFactory.getBeanFactory(), 
MQueryWrapper.class, 
  response.getText());
  MQuery mQuery = bean.as().getMquery();
  logger.log(Level.INFO, mQuery.getScreenname());
  screenName = mQuery.getScreenname();
  fieldList = mQuery.getFields().getField();
  view.renderFields(fieldList);

Client-side this beats overlay types because it can handle nested objects 
(so long as I figure out nesting). I've not tried it server-side yet, but 
that looks *very* handy. However it's probably wise at this early stage to 
come up with better tags than field and fields for the XML that my JSON 
originates from. As more structures are added, I may have conflicts. (I 
wonder if JSON's XML.toJSONObject() does some magic with namespaces...)

On Tuesday, August 20, 2013 5:34:03 AM UTC-4, Thomas Broyer wrote:

 You don't seem to have an AutoBean interface with a MQuery getMquery() 
 method.
 The object you're parsing has only a mquery property, so if you look at 
 it as if it were an MQuery object, getScreenname and getFields would 
 expectedly be null.

 On Tuesday, August 20, 2013 4:37:30 AM UTC+2, Thad Humphries wrote:

 I've managed a simple AutoBean with a list. Now I've a more complex one, 
 and I don't understand what's wrong. My get methods are returning null 
 though the debugger shows the object has data.

 The JSON looks like this:

 {
   mquery:
 {
   screenname:Index Card,
   fields:
 {
   field:[
 {title:Name,  name:odname,   value:*TIFF*},
 {title:Date,  name:oddate,   value:},
 {title:Ref #, name:odrefnum, value:}
   ]
 }
 }
 } 


 I have three interfaces:

 public interface Field {
   String getTitle();
   void setTitle(String title);
   String getName();
   void setName(String name);
   String getValue();
   void setValue(String value);
 }

 public interface FieldList {
   ListField getField();
   void setField(ListField field);
 }

 public interface MQuery {
   String getScreenname();
   void setScreenname(String screenname);
   FieldList getFields();
   void setFields(FieldList fields);
 }

 In my bean factory, I have

 public interface BeanFactory extends AutoBeanFactory {
   ...
   AutoBeanField field();
   AutoBeanFieldList fields();
   AutoBeanMQuery mquery();
 }

 In my onResponseReceived() method, I call

   AutoBeanMQuery mqueryBean = 
   AutoBeanCodex.decode(clientFactory.getBeanFactory(), 
   MQuery.class, response.getText());
   MQuery mQuery = mqueryBean.as();
   logger.log(Level.INFO, mQuery.getScreenname());
   screenName = mQuery.getScreenname();
   fieldList = mQuery.getFields().getField();
   view.renderFields(fieldList);

 When I look at the debugger in Chrome, screenname, etc are populated (in 
 mQuery:this$0:data_0 there is mquery with screenename set and 3 field 
 objects in fields). However mQuery.getScreenname() returns null as does 
 mQuery.getFields().

 What am I not seeing here? Could the presence of other AutoBeans in my 
 factory (beans that work) be messing me up (do I need a separate factory)?



-- 
You received this message because you are subscribed to the Google Groups 
Google Web Toolkit group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit+unsubscr...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.


Re: Problem decoding complex AutoBean

2013-08-20 Thread Thomas Broyer


On Tuesday, August 20, 2013 1:47:58 PM UTC+2, Thad Humphries wrote:

 Thank you, Thomas. That worked.

 I added the interface

 public interface MQueryWrapper {
   MQuery getMquery();
 }

 In BeanFactory, I changed AutoBeanMQuery mquery(); for 
 AutoBeanMQueryWrapper mquery();

 My onResponseReceived() now looks like

   AutoBeanMQueryWrapper bean = 
   AutoBeanCodex.decode(clientFactory.getBeanFactory(), 
 MQueryWrapper.class, 
   response.getText());
   MQuery mQuery = bean.as().getMquery();
   logger.log(Level.INFO, mQuery.getScreenname());
   screenName = mQuery.getScreenname();
   fieldList = mQuery.getFields().getField();
   view.renderFields(fieldList);

 Client-side this beats overlay types because it can handle nested objects 
 (so long as I figure out nesting).


Why wouldn't you be able to do it with JSOs?
 

 I've not tried it server-side yet, but that looks *very* handy.


Yup, I'm using it to serialize some 
datahttps://github.com/tbroyer/gwt-maven-archetypes/blob/598bed4f16a52d97b21a5f0fa9a3ce65650903a4/guice-rf-activities/src/main/resources/archetype-resources/__rootArtifactId__-server/src/main/java/ServerUser.java#L29that
 I output 
in the host 
pagehttps://github.com/tbroyer/gwt-maven-archetypes/blob/598bed4f16a52d97b21a5f0fa9a3ce65650903a4/guice-rf-activities/src/main/resources/archetype-resources/__rootArtifactId__-server/src/main/webapp/index.jsp#L70
 and 
load on the client-side at 
startuphttps://github.com/tbroyer/gwt-maven-archetypes/blob/598bed4f16a52d97b21a5f0fa9a3ce65650903a4/guice-rf-activities/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/__module__GinModule.java#L74,
 
works great and you're sure that your JSON will be understood by the end 
and don't fear forgetting to update the code on one side and not the other.

-- 
You received this message because you are subscribed to the Google Groups 
Google Web Toolkit group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit+unsubscr...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.


Re: Problem decoding complex AutoBean

2013-08-20 Thread Thad Humphries
On Tuesday, August 20, 2013 8:40:47 AM UTC-4, Thomas Broyer wrote:



 On Tuesday, August 20, 2013 1:47:58 PM UTC+2, Thad Humphries wrote:

 Thank you, Thomas. That worked.

 I added the interface

 public interface MQueryWrapper {
   MQuery getMquery();
 }

 In BeanFactory, I changed AutoBeanMQuery mquery(); for 
 AutoBeanMQueryWrapper mquery();

 My onResponseReceived() now looks like

   AutoBeanMQueryWrapper bean = 
   AutoBeanCodex.decode(clientFactory.getBeanFactory(), 
 MQueryWrapper.class, 
   response.getText());
   MQuery mQuery = bean.as().getMquery();
   logger.log(Level.INFO, mQuery.getScreenname());
   screenName = mQuery.getScreenname();
   fieldList = mQuery.getFields().getField();
   view.renderFields(fieldList);

 Client-side this beats overlay types because it can handle nested objects 
 (so long as I figure out nesting).


 Why wouldn't you be able to do it with JSOs?


Ah, thank you for that little programming problem. I was missing something 
when I previously looked at overlay types. Yes, they can handle nested 
objects.

Assuming the same JSON structure as above, these two objects manage it:

public class MQueryField extends JavaScriptObject {
  protected MQueryField() {}
  public final native String getTitle() /*-{ return this.title; }-*/;
  public final native String getName() /*-{ return this.name; }-*/;
  public final native String getValue() /*-{ return this.value; }-*/;
}

public class MQueryRoot extends JavaScriptObject {
  protected MQueryRoot() {}
  public final native String getScreenName() /*-{ return this.screenname; 
}-*/;
  public final native JsArrayMQueryField getFields() 
  /*-{ return this.fields.field; }-*/;
}

Now reading in that same string:

JSONObject jobj = new JSONObject(JsonUtils.safeEval(response.getText()));
MQueryRoot root = 
jobj.get(mquery).isObject().getJavaScriptObject().cast();
logger.log(Level.INFO, root.getScreenName());
// prints Index Card
logger.log(Level.INFO, fields[+root.getFields().length()+]);
// prints fields[3]

Hm… I think I prefer AutoBeans, but this is nice to know. 

 

 I've not tried it server-side yet, but that looks *very* handy.


 Yup, I'm using it to serialize some 
 datahttps://github.com/tbroyer/gwt-maven-archetypes/blob/598bed4f16a52d97b21a5f0fa9a3ce65650903a4/guice-rf-activities/src/main/resources/archetype-resources/__rootArtifactId__-server/src/main/java/ServerUser.java#L29that
  I output 
 in the host 
 pagehttps://github.com/tbroyer/gwt-maven-archetypes/blob/598bed4f16a52d97b21a5f0fa9a3ce65650903a4/guice-rf-activities/src/main/resources/archetype-resources/__rootArtifactId__-server/src/main/webapp/index.jsp#L70
  and 
 load on the client-side at 
 startuphttps://github.com/tbroyer/gwt-maven-archetypes/blob/598bed4f16a52d97b21a5f0fa9a3ce65650903a4/guice-rf-activities/src/main/resources/archetype-resources/__rootArtifactId__-client/src/main/java/__module__GinModule.java#L74,
  
 works great and you're sure that your JSON will be understood by the end 
 and don't fear forgetting to update the code on one side and not the other.


Thank you for these examples.
 

-- 
You received this message because you are subscribed to the Google Groups 
Google Web Toolkit group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit+unsubscr...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.


Re: Problem decoding complex AutoBean

2013-08-20 Thread Jens


 JSONObject jobj = new JSONObject(JsonUtils.safeEval(response.getText()));
 MQueryRoot root = 
 jobj.get(mquery).isObject().getJavaScriptObject().cast();


If you want that code a bit easier to read you could use a MQueryWrapper 
JSO like in the AutoBean example and then write

MQueryWrapper wrapper = JsonUtils.safeEval(response.getText());
MQueryRoot root = wrapper.getMQuery();


So at the end its like: calling .as() vs. implementing JSO's by hand/via 
code generation. 

-- J.

-- 
You received this message because you are subscribed to the Google Groups 
Google Web Toolkit group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit+unsubscr...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.


Problem decoding complex AutoBean

2013-08-19 Thread Thad Humphries
I've managed a simple AutoBean with a list. Now I've a more complex one, 
and I don't understand what's wrong. My get methods are returning null 
though the debugger shows the object has data.

The JSON looks like this:

{
  mquery:
{
  screenname:Index Card,
  fields:
{
  field:[
{title:Name,  name:odname,   value:*TIFF*},
{title:Date,  name:oddate,   value:},
{title:Ref #, name:odrefnum, value:}
  ]
}
}
} 


I have three interfaces:

public interface Field {
  String getTitle();
  void setTitle(String title);
  String getName();
  void setName(String name);
  String getValue();
  void setValue(String value);
}

public interface FieldList {
  ListField getField();
  void setField(ListField field);
}

public interface MQuery {
  String getScreenname();
  void setScreenname(String screenname);
  FieldList getFields();
  void setFields(FieldList fields);
}

In my bean factory, I have

public interface BeanFactory extends AutoBeanFactory {
  ...
  AutoBeanField field();
  AutoBeanFieldList fields();
  AutoBeanMQuery mquery();
}

In my onResponseReceived() method, I call

  AutoBeanMQuery mqueryBean = 
  AutoBeanCodex.decode(clientFactory.getBeanFactory(), 
  MQuery.class, response.getText());
  MQuery mQuery = mqueryBean.as();
  logger.log(Level.INFO, mQuery.getScreenname());
  screenName = mQuery.getScreenname();
  fieldList = mQuery.getFields().getField();
  view.renderFields(fieldList);

When I look at the debugger in Chrome, screenname, etc are populated (in 
mQuery:this$0:data_0 there is mquery with screenename set and 3 field 
objects in fields). However mQuery.getScreenname() returns null as does 
mQuery.getFields().

What am I not seeing here? Could the presence of other AutoBeans in my 
factory (beans that work) be messing me up (do I need a separate factory)?

-- 
You received this message because you are subscribed to the Google Groups 
Google Web Toolkit group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to google-web-toolkit+unsubscr...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.