Wolfgang Fahl
Wolfgang Fahl

Reputation: 15776

Simple - XML reference resolving

The original reason for my Jaxb Question JaxB reference resolving

was that I couldn't get the same issue working with the simple-framework:

http://old.nabble.com/Two-Phase-support-for-CycleStrategy--td34802791.html

Today I got the things working with a persister call back to the same point as in my Jaxb Questions: I get copies - not references. Again I am looking for a solution with proper references. This time for the Simple XML framework.

The example here has the base class "ModelElement" not Person as in the other question. Otherwise the problem is the same.

Again I am calling the unmarshalling twice to get all ids in PASS 1 and use the gathered results from the lookup HashMap created in PASS2.

What would be a solution to get proper references? My assumption is that adding a call back that actually lets the called function modify the unmarshalling result (see How to use an output parameter in Java? for a wrapping approach) would do the trick (comparable to the JaxB solution I have posted in the meantime).

Persister serializer = new Persister();
ModelElementSimpleXmlImpl.lookup.clear();
serializer.read(result, xml);
System.err.println("PASS 2");
serializer.read(result, xml);

This code is from the ModelElementSimpleXmlImpl base class: ...

  protected String ref;

  /**
   * getter for xsd:string/String id
   * @return id
   */
  @org.simpleframework.xml.Attribute(name="ref",required=false)
  public String getRef() { 
    return ref; 
  }

  /**
   * setter for xsd:string/String id
   * @param pid - new value for id
   */
  @org.simpleframework.xml.Attribute(name="ref",required=false)
  public void setRef(String pRef) { 
    ref=pRef; 
  }

  private boolean debug=true;
  /**
     * show debug information
     * @param title
     * @param key
     * @param me
     * @param found
     */
    public void showDebug(String title,String key,ModelElementSimpleXmlImpl me, ModelElementSimpleXmlImpl found) {
        String deref="?";
        if (found!=null)
            deref="->"+found.getId()+"("+found.getClass().getSimpleName()+")";
        if (debug)
            System.err.println(title+": "+key+"("+me.getClass().getSimpleName()+")"+deref+" - "+this);
    }
    /**
     * keep track of the elements already seen
     */
    public static Map<String,ModelElementSimpleXmlImpl> lookup=new HashMap<String,ModelElementSimpleXmlImpl>();

  @Validate
  public void validate() {
    ModelElementSimpleXmlImpl me=this;
    String key=me.getId();
        if (key!=null) {
            showDebug("id",key,me,null);
            lookup.put(key, me);
        }
        key=me.getRef();
        if (key!=null) {
            if (lookup.containsKey(key)) {
                ModelElementSimpleXmlImpl meRef=lookup.get(key);
                showDebug("ref",key,me,meRef);
                me.setRef(null);
        me.copyFrom(meRef);
            } else {
                if (debug)
                    showDebug("ref",me.getRef(),me,null);
            }
        }
  }

Upvotes: 3

Views: 1548

Answers (1)

Wolfgang Fahl
Wolfgang Fahl

Reputation: 15776

Niall Gallagher suggested:

Should be fairly easy to do with something like the CycleStrategy. Just create MyCycleStrategy and where there is an exception with "Invalid reference" just return null and remember the reference. When you have picked up all the ids and the values then do a second pass. In the second pass assign the value to the first occurrence of either the ref or the id. Then all following references should be given the same value. This should work.

And he is right. The following extended Cycle Strategy works as proposed:

/**
 * Two Path Cycle Strategy
 * 
 * @author wf
 * 
 */
public static class TwoPathCycleStrategy extends CycleStrategy {
    String id;
    String ref;
    public static boolean debug = false;

    /**
     * create a twoPath Cycle Strategy
     * 
     * @param id
     * @param ref
     */
    public TwoPathCycleStrategy(String id, String ref) {
        super(id, ref);
        this.id = id;
        this.ref = ref;
    }

    /**
     * show debug information
     * 
     * @param title
     * @param key
     * @param value
     */
    public void showDebug(String title, String key, Value value) {
        if (debug) {
            String id = "?";
            Object v = value;
            while ((v instanceof Value) && ((Value) v).isReference()) {
                v = ((Value) v).getValue();
            }
            if (v == null) {
                id = "null";
            } else {
                // FIXME - adapt to your code or comment out
                //if (v instanceof ModelElement) {
                //  ModelElement me = (ModelElement) v;
                //  id = me.getId();
                //}
            }
            System.err.println(title + ":" + key + "->"
                    + v.getClass().getSimpleName() + ":"
                    + value.getType().getSimpleName() + ":" + value.isReference() + ":"
                    + id);
        }
    }

    public Map<String, Value> lookup = new HashMap<String, Value>();

    @SuppressWarnings("rawtypes")
    @Override
    public Value read(Type type, NodeMap node, Map map) throws Exception {
        Value value = null;
        Node refNode = node.get(ref);
        Node keyNode = node.get(id);
        try {
            value = super.read(type, node, map);
            if (keyNode != null) {
                String key = keyNode.getValue();
                if (value != null) {
                    showDebug("id", key, value);
                    lookup.put(key, value);
                } else {
                    showDebug("id?", key, value);
                }
            }
        } catch (CycleException ce) {
            if (ce.getMessage().contains("Invalid reference")) {
                if (refNode != null) {
                    String key = refNode.getValue();
                    if (lookup.containsKey(key)) {
                        value = lookup.get(key);
                        showDebug("ref", key, value);
                    } else {
                        showDebug("ref?",key,null);
                    }
                }
            } else {
                throw ce;
            }
        }
        return value;
    }

}

Upvotes: 2

Related Questions