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}