001/* 002 * AnnotationStrategy.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>AnnotationStrategy</code> object is used to intercept 033 * the serialization process and delegate to custom converters. This 034 * strategy uses the <code>Convert</code> annotation to specify the 035 * converter to use for serialization and deserialization. If there 036 * is no annotation present on the field or method representing the 037 * object instance to be serialized then this acts as a transparent 038 * proxy to an internal strategy. 039 * <p> 040 * By default the <code>TreeStrategy</code> is used to perform the 041 * normal serialization process should there be no annotation 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.strategy.TreeStrategy 050 */ 051public class AnnotationStrategy implements Strategy { 052 053 /** 054 * This is used to scan for an annotation and create a converter. 055 */ 056 private final ConverterScanner scanner; 057 058 /** 059 * This is the strategy that is delegated to for serialization. 060 */ 061 private final Strategy strategy; 062 063 /** 064 * Constructor for the <code>AnnotationStrategy</code> object. 065 * This creates a strategy that intercepts serialization on any 066 * annotated method or field. If no annotation exists then this 067 * delegates to an internal <code>TreeStrategy</code> object. 068 */ 069 public AnnotationStrategy() { 070 this(new TreeStrategy()); 071 } 072 073 /** 074 * Constructor for the <code>AnnotationStrategy</code> object. 075 * This creates a strategy that intercepts serialization on any 076 * annotated method or field. If no annotation exists then this 077 * will delegate to the <code>Strategy</code> provided. 078 * 079 * @param strategy the internal strategy to delegate to 080 */ 081 public AnnotationStrategy(Strategy strategy) { 082 this.scanner = new ConverterScanner(); 083 this.strategy = strategy; 084 } 085 086 /** 087 * This is used to read the <code>Value</code> which will be used 088 * to represent the deserialized object. If there is an annotation 089 * present then the value will contain an object instance. If it 090 * does not then it is up to the internal strategy to determine 091 * what the returned value contains. 092 * 093 * @param type this is the type that represents a method or field 094 * @param node this is the node representing the XML element 095 * @param map this is the session map that contain variables 096 * 097 * @return the value representing the deserialized value 098 */ 099 public Value read(Type type, NodeMap<InputNode> node, Map map) throws Exception { 100 Value value = strategy.read(type, node, map); 101 102 if(isReference(value)) { 103 return value; 104 } 105 return read(type, node, value); 106 } 107 108 /** 109 * This is used to read the <code>Value</code> which will be used 110 * to represent the deserialized object. If there is an annotation 111 * present then the value will contain an object instance. If it 112 * does not then it is up to the internal strategy to determine 113 * what the returned value contains. 114 * 115 * @param type this is the type that represents a method or field 116 * @param node this is the node representing the XML element 117 * @param value this is the value from the internal strategy 118 * 119 * @return the value representing the deserialized value 120 */ 121 private Value read(Type type, NodeMap<InputNode> node, Value value) throws Exception { 122 Converter converter = scanner.getConverter(type, value); 123 InputNode parent = node.getNode(); 124 125 if(converter != null) { 126 Object data = converter.read(parent); 127 Class actual = type.getType(); 128 129 if(value != null) { 130 value.setValue(data); 131 } 132 return new Reference(value, data, actual); 133 } 134 return value; 135 } 136 137 /** 138 * This is used to serialize a representation of the object value 139 * provided. If there is a <code>Convert</code> annotation present 140 * on the provided type then this will use the converter specified 141 * to serialize a representation of the object. If however there 142 * is no annotation then this will delegate to the internal 143 * strategy. This returns true if the serialization has completed. 144 * 145 * @param type this is the type that represents the field or method 146 * @param value this is the object instance to be serialized 147 * @param node this is the XML element to be serialized to 148 * @param map this is the session map used by the serializer 149 * 150 * @return this returns true if it was serialized, false otherwise 151 */ 152 public boolean write(Type type, Object value, NodeMap<OutputNode> node, Map map) throws Exception { 153 boolean reference = strategy.write(type, value, node, map); 154 155 if(!reference) { 156 return write(type, value, node); 157 } 158 return reference; 159 } 160 161 /** 162 * This is used to serialize a representation of the object value 163 * provided. If there is a <code>Convert</code> annotation present 164 * on the provided type then this will use the converter specified 165 * to serialize a representation of the object. If however there 166 * is no annotation then this will delegate to the internal 167 * strategy. This returns true if the serialization has completed. 168 * 169 * @param type this is the type that represents the field or method 170 * @param value this is the object instance to be serialized 171 * @param node this is the XML element to be serialized to 172 * 173 * @return this returns true if it was serialized, false otherwise 174 */ 175 private boolean write(Type type, Object value, NodeMap<OutputNode> node) throws Exception { 176 Converter converter = scanner.getConverter(type, value); 177 OutputNode parent = node.getNode(); 178 179 if(converter != null) { 180 converter.write(parent, value); 181 return true; 182 } 183 return false; 184 } 185 186 /** 187 * This is used to determine if the <code>Value</code> provided 188 * represents a reference. If it does represent a reference then 189 * this will return true, if it does not then this returns false. 190 * 191 * @param value this is the value instance to be evaluated 192 * 193 * @return this returns true if the value represents a reference 194 */ 195 private boolean isReference(Value value) { 196 return value != null && value.isReference(); 197 } 198}