2

What is the best way to make a deep copy of a gwt overlay type?

I'm looking for a function or library that inspects a GWT overlay and clones it. It must be able to clone contained arrays or objects.

Thanks

Javier Ferrero
  • 8,741
  • 8
  • 45
  • 50

3 Answers3

5

There are 2 ways that I would consider. Most of the time overlay objects are used in conjunction with JSON, so you could just stringify the object and parse the results:

public native MyOverlayType deepCopy()/*-{
  return JSON.parse(JSON.stringify(this));
}-*/;

OR

public static native MyOverlayType fromJson(String json)/*-{
      return JSON.parse(json);
}-*/;
public native String getJson()/*-{
      return JSON.stringify(this);
}-*/;
public MyOverlayType deepCopy(){
      return fromJson(getJson());
}

The other option is a pure javascript approach which will maintain other stuff such as function pointers and probably be more efficient.

public class JsoUtils
{
    @SuppressWarnings("unchecked")
    public static <T extends JavaScriptObject> T deepCopy(T obj)
    {
        return (T) deepCopyImpl(obj);
    }

    private static native JavaScriptObject deepCopyImpl(JavaScriptObject obj)/*-{
        if (typeof obj !== 'object' || obj === null) {
            return obj;
        }

        var c = obj instanceof Array ? [] : {};

        for (var i in obj) {
            if (obj.hasOwnProperty(i)) {
                if (typeof obj[i] !== 'object' || obj[i] === null)
                    c[i] = obj[i];
                else
                    c[i] = @com.example.gwt.client.JsoUtils::deepCopyImpl(Lcom/google/gwt/core/client/JavaScriptObject;)(obj[i]);
            }
        }

        return c;
    }-*/;
}
LINEMAN78
  • 2,562
  • 16
  • 19
  • Where do you define clone_obj function? The use of recursiveness with JSNI is one of the things I find more difficult. – Javier Ferrero Jan 19 '11 at 00:49
  • Aw, sorry didn't catch that on my copy... just a sec will update – LINEMAN78 Jan 19 '11 at 01:01
  • +1 Thanks, your function was of great help. I modified it to eliminate the unnecesary type checking, support copying Dates and iterate the arrays in a non-associative way. – Javier Ferrero Jan 19 '11 at 14:06
2

Based on Lineman78's answer and taking into consideration this other answer from A. Levy I created the following function:

public class JsoUtils {

    @SuppressWarnings("unchecked")
    public static <T extends JavaScriptObject> T deepCopy(T obj)
    {
        return (T) deepCopyImpl(obj);
    }

    private static native JavaScriptObject deepCopyImpl(JavaScriptObject obj) /*-{

        if (obj == null) return obj;

        var copy;        

        if (obj instanceof Date) {
            // Handle Date
            copy = new Date();
            copy.setTime(obj.getTime());
        } else if (obj instanceof Array) {
             // Handle Array
            copy = [];
            for (var i = 0, len = obj.length; i < len; i++) {
                if (obj[i] == null || typeof obj[i] != "object") copy[i] = obj[i];
                else copy[i] = @com.amindea.noah.client.utils.JsoUtils::deepCopyImpl(Lcom/google/gwt/core/client/JavaScriptObject;)(obj[i]);
            }
        } else {
            // Handle Object
            copy = {};
            for (var attr in obj) {
                if (obj.hasOwnProperty(attr)) {
                    if (obj[attr] == null || typeof obj[attr] != "object") copy[attr] = obj[attr];
                    else copy[attr] = @com.amindea.noah.client.utils.JsoUtils::deepCopyImpl(Lcom/google/gwt/core/client/JavaScriptObject;)(obj[attr]);
                }
            }
        }        
        return copy;
    }-*/;

} 

It supports deep copy of Object, Array, Date, String, Number, or Boolean. As explained by A. Levy the function will work as long as the data in the objects and arrays form a tree structure.

Community
  • 1
  • 1
Javier Ferrero
  • 8,741
  • 8
  • 45
  • 50
0

I found the simplest way to clone a JavaScriptObject is using the JsonUtils class provided by GWT:

import com.google.gwt.core.client.JsonUtils;

final String taskJson = JsonUtils.stringify(selectedTask);
TaskJso task = JsonUtils.safeEval(taskJson).cast();
Jake W
  • 2,788
  • 34
  • 39