001/*
002 * CycleStrategy.java April 2007
003 *
004 * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
015 * implied. See the License for the specific language governing 
016 * permissions and limitations under the License.
017 */
018
019package org.simpleframework.xml.strategy;
020
021import static org.simpleframework.xml.strategy.Name.LABEL;
022import static org.simpleframework.xml.strategy.Name.LENGTH;
023import static org.simpleframework.xml.strategy.Name.MARK;
024import static org.simpleframework.xml.strategy.Name.REFER;
025
026import java.util.Map;
027
028import org.simpleframework.xml.stream.NodeMap;
029
030/**
031 * The <code>CycleStrategy</code> represents a strategy that is used
032 * to augment the deserialization and serialization process such that
033 * cycles in an object graph can be supported. This adds additional 
034 * attributes to the serialized XML elements so that during the 
035 * deserialization process an objects cycles can be created. Without
036 * the use of a strategy such as this, cycles could cause an infinite
037 * loop during the serialization process while traversing the graph.
038 * <pre>
039 * 
040 *    &lt;root id="1"&gt;
041 *       &lt;object id="2"&gt;
042 *          &lt;object id="3" name="name"&gt;Example&lt;/item&gt;
043 *          &lt;object reference="2"/&gt;
044 *       &lt;/object&gt;
045 *    &lt;/root&gt;
046 * 
047 * </pre>
048 * In the above serialized XML there is a circular reference, where
049 * the XML element with id "2" contains a reference to itself. In
050 * most data binding frameworks this will cause an infinite loop, 
051 * or in some cases will just fail to represent the references well.
052 * With this strategy you can ensure that cycles in complex object
053 * graphs will be maintained and can be serialized safely.
054 * 
055 * @author Niall Gallagher
056 * 
057 * @see org.simpleframework.xml.core.Persister
058 * @see org.simpleframework.xml.strategy.Strategy
059 */
060public class CycleStrategy implements Strategy {
061   
062   /**
063    * This is used to maintain session state for writing the graph.
064    */
065   private final WriteState write;
066   
067   /**
068    * This is used to maintain session state for reading the graph.
069    */
070   private final ReadState read;
071   
072   /**
073    * This is used to provide the names of the attributes to use.
074    */
075   private final Contract contract;
076   
077   /**
078    * Constructor for the <code>CycleStrategy</code> object. This is
079    * used to create a strategy with default values. By default the
080    * values used are "id" and "reference". These values will be
081    * added to XML elements during the serialization process. And 
082    * will be used to deserialize the object cycles fully.
083    */
084   public CycleStrategy() {
085      this(MARK, REFER);
086   }
087   
088   /**
089    * Constructor for the <code>CycleStrategy</code> object. This is
090    * used to create a strategy with the specified attributes, which
091    * will be added to serialized XML elements. These attributes 
092    * are used to serialize the objects in such a way the cycles in
093    * the object graph can be deserialized and used fully. 
094    * 
095    * @param mark this is used to mark the identity of an object
096    * @param refer this is used to refer to an existing object
097    */
098   public CycleStrategy(String mark, String refer) {
099      this(mark, refer, LABEL);      
100   }
101   
102   /**
103    * Constructor for the <code>CycleStrategy</code> object. This is
104    * used to create a strategy with the specified attributes, which
105    * will be added to serialized XML elements. These attributes 
106    * are used to serialize the objects in such a way the cycles in
107    * the object graph can be deserialized and used fully. 
108    * 
109    * @param mark this is used to mark the identity of an object
110    * @param refer this is used to refer to an existing object
111    * @param label this is used to specify the class for the field
112    */   
113   public CycleStrategy(String mark, String refer, String label){
114      this(mark, refer, label, LENGTH);
115   }
116   
117   /**
118    * Constructor for the <code>CycleStrategy</code> object. This is
119    * used to create a strategy with the specified attributes, which
120    * will be added to serialized XML elements. These attributes 
121    * are used to serialize the objects in such a way the cycles in
122    * the object graph can be deserialized and used fully. 
123    * 
124    * @param mark this is used to mark the identity of an object
125    * @param refer this is used to refer to an existing object
126    * @param label this is used to specify the class for the field
127    * @param length this is the length attribute used for arrays
128    */   
129   public CycleStrategy(String mark, String refer, String label, String length){
130      this.contract = new Contract(mark, refer, label, length);
131      this.write = new WriteState(contract);
132      this.read = new ReadState(contract);
133   } 
134   
135   /**
136    * This method is used to read an object from the specified node.
137    * In order to get the root type the field and node map are 
138    * specified. The field represents the annotated method or field
139    * within the deserialized object. The node map is used to get
140    * the attributes used to describe the objects identity, or in
141    * the case of an existing object it contains an object reference.
142    * 
143    * @param type the method or field in the deserialized object
144    * @param node this is the XML element attributes to read
145    * @param map this is the session map used for deserialization
146    * 
147    * @return this returns an instance to insert into the object 
148    */
149   public Value read(Type type, NodeMap node, Map map) throws Exception {
150      ReadGraph graph = read.find(map);
151      
152      if(graph != null) {
153         return graph.read(type, node);
154      }
155      return null;
156   }
157   
158   /**
159    * This is used to write the reference in to the XML element that 
160    * is to be written. This will either insert an object identity if
161    * the object has not previously been written, or, if the object
162    * has already been written in a previous element, this will write
163    * the reference to that object. This allows all cycles within the
164    * graph to be serialized so that they can be fully deserialized. 
165    * 
166    * @param type the type of the field or method in the object
167    * @param value this is the actual object that is to be written
168    * @param node this is the XML element attribute map to use
169    * @param map this is the session map used for the serialization
170    * 
171    * @return returns true if the object has been fully serialized
172    */
173   public boolean write(Type type, Object value, NodeMap node, Map map){
174      WriteGraph graph = write.find(map);
175      
176      if(graph != null) {
177         return graph.write(type, value, node);
178      }
179      return false;
180   }
181}