001/*
002 * RegistryStrategy.java January 2010
003 *
004 * Copyright (C) 2010, 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.convert;
020
021import java.util.Map;
022
023import org.simpleframework.xml.strategy.Strategy;
024import org.simpleframework.xml.strategy.TreeStrategy;
025import org.simpleframework.xml.strategy.Type;
026import org.simpleframework.xml.strategy.Value;
027import org.simpleframework.xml.stream.InputNode;
028import org.simpleframework.xml.stream.NodeMap;
029import org.simpleframework.xml.stream.OutputNode;
030
031/**
032 * The <code>RegistryStrategy</code> object is used to intercept
033 * the serialization process and delegate to custom converters. The
034 * custom converters are resolved from a <code>Registry</code>
035 * object, which is provided to the constructor. If there is no
036 * binding for a particular object then serialization is delegated
037 * to an internal strategy. All converters resolved by this are
038 * instantiated once and cached internally for performance.
039 * <p>
040 * By default the <code>TreeStrategy</code> is used to perform the
041 * normal serialization process should there be no class binding
042 * specifying a converter to use. However, any implementation can
043 * be used, including the <code>CycleStrategy</code>, which handles
044 * cycles in the object graph. To specify the internal strategy to
045 * use it can be provided in the constructor.
046 * 
047 * @author Niall Gallagher
048 *
049 * @see org.simpleframework.xml.convert.Registry
050 */
051public class RegistryStrategy implements Strategy {
052   
053   /**
054    * This is the registry that is used to resolve bindings.
055    */
056   private final Registry registry;
057   
058   /**
059    * This is the strategy used if there is no bindings.
060    */
061   private final Strategy strategy;
062   
063   /**
064    * Constructor for the <code>RegistryStrategy</code> object. This
065    * is used to create a strategy that will intercept the normal
066    * serialization process by searching for bindings within the
067    * provided <code>Registry</code> instance.
068    * 
069    * @param registry this is the registry instance with bindings
070    */
071   public RegistryStrategy(Registry registry) {
072      this(registry, new TreeStrategy());
073   }
074   
075   /**
076    * Constructor for the <code>RegistryStrategy</code> object. This
077    * is used to create a strategy that will intercept the normal
078    * serialization process by searching for bindings within the
079    * provided <code>Registry</code> instance.
080    * 
081    * @param registry this is the registry instance with bindings
082    * @param strategy this is the strategy to delegate to
083    */
084   public RegistryStrategy(Registry registry, Strategy strategy){
085      this.registry = registry;
086      this.strategy = strategy;
087   }
088   
089   /**
090    * This is used to read the <code>Value</code> which will be used 
091    * to represent the deserialized object. If there is an binding
092    * present then the value will contain an object instance. If it
093    * does not then it is up to the internal strategy to determine 
094    * what the returned value contains.
095    * 
096    * @param type this is the type that represents a method or field
097    * @param node this is the node representing the XML element
098    * @param map this is the session map that contain variables
099    * 
100    * @return the value representing the deserialized value
101    */
102   public Value read(Type type, NodeMap<InputNode> node, Map map) throws Exception {
103      Value value = strategy.read(type, node, map);
104      
105      if(isReference(value)) {
106         return value;
107      }
108      return read(type, node, value);
109   }
110  
111   /**
112    * This is used to read the <code>Value</code> which will be used 
113    * to represent the deserialized object. If there is an binding
114    * present then the value will contain an object instance. If it
115    * does not then it is up to the internal strategy to determine 
116    * what the returned value contains.
117    * 
118    * @param type this is the type that represents a method or field
119    * @param node this is the node representing the XML element
120    * @param value this is the value from the internal strategy
121    * 
122    * @return the value representing the deserialized value
123    */   
124   private Value read(Type type, NodeMap<InputNode> node, Value value) throws Exception {
125      Converter converter = lookup(type, value);
126      InputNode source = node.getNode();
127      
128      if(converter != null) {
129         Object data = converter.read(source);
130         Class actual = type.getType();
131      
132         if(value != null) {
133            value.setValue(data);
134         }
135         return new Reference(value, data, actual);
136      }
137      return value;
138   }
139   
140   /**
141    * This is used to serialize a representation of the object value
142    * provided. If there is a <code>Registry</code> binding present
143    * for the provided type then this will use the converter specified
144    * to serialize a representation of the object. If however there
145    * is no binding present then this will delegate to the internal 
146    * strategy. This returns true if the serialization has completed.
147    * 
148    * @param type this is the type that represents the field or method
149    * @param value this is the object instance to be serialized
150    * @param node this is the XML element to be serialized to
151    * @param map this is the session map used by the serializer
152    * 
153    * @return this returns true if it was serialized, false otherwise
154    */
155   public boolean write(Type type, Object value, NodeMap<OutputNode> node, Map map) throws Exception {
156      boolean reference = strategy.write(type, value, node, map);
157      
158      if(!reference) {
159         return write(type, value, node);
160      }
161      return reference;
162   }
163   
164   /**
165    * This is used to serialize a representation of the object value
166    * provided. If there is a <code>Registry</code> binding present
167    * for the provided type then this will use the converter specified
168    * to serialize a representation of the object. If however there
169    * is no binding present then this will delegate to the internal 
170    * strategy. This returns true if the serialization has completed.
171    * 
172    * @param type this is the type that represents the field or method
173    * @param value this is the object instance to be serialized
174    * @param node this is the XML element to be serialized to
175    * 
176    * @return this returns true if it was serialized, false otherwise
177    */
178   private boolean write(Type type, Object value, NodeMap<OutputNode> node) throws Exception {
179      Converter converter = lookup(type, value);
180      OutputNode source = node.getNode();
181      
182      if(converter != null) {
183         converter.write(source, value);
184         return true;
185      }
186      return false;  
187   }
188   
189   /**
190    * This is used to acquire a <code>Converter</code> instance for 
191    * the provided value object. The value object is used to resolve
192    * the converter to use for the serialization process.
193    * 
194    * @param type this is the type representing the field or method
195    * @param value this is the value that is to be serialized
196    * 
197    * @return this returns the converter instance that is matched
198    */
199   private Converter lookup(Type type, Value value) throws Exception {
200      Class real = type.getType();
201      
202      if(value != null) {
203         real = value.getType();
204      }
205      return registry.lookup(real);
206   }
207   
208   /**
209    * This is used to acquire a <code>Converter</code> instance for 
210    * the provided object instance. The instance class is used to
211    * resolve the converter to use for the serialization process.
212    * 
213    * @param type this is the type representing the field or method
214    * @param value this is the value that is to be serialized
215    * 
216    * @return this returns the converter instance that is matched
217    */
218   private Converter lookup(Type type, Object value) throws Exception {
219      Class real = type.getType();
220      
221      if(value != null) {
222         real = value.getClass();
223      }
224      return registry.lookup(real);
225   }
226   
227   /**
228    * This is used to determine if the <code>Value</code> provided
229    * represents a reference. If it does represent a reference then
230    * this will return true, if it does not then this returns false.
231    * 
232    * @param value this is the value instance to be evaluated
233    * 
234    * @return this returns true if the value represents a reference
235    */
236   private boolean isReference(Value value) {
237      return value != null && value.isReference();
238   }
239}