001/* 002 * Transformer.java May 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.transform; 020 021import java.util.Map; 022 023import org.simpleframework.xml.util.Cache; 024import org.simpleframework.xml.util.ConcurrentCache; 025 026/** 027 * The <code>Transformer</code> object is used to convert strings to 028 * and from object instances. This is used during the serialization 029 * and deserialization process to transform types from the Java class 030 * libraries, as well as other types which do not contain XML schema 031 * annotations. Typically this will be used to transform primitive 032 * types to and from strings, such as <code>int</code> values. 033 * <pre> 034 * 035 * @Element 036 * private String[] value; 037 * 038 * </pre> 039 * For example taking the above value the array of strings needs to 040 * be converted in to a single string value that can be inserted in 041 * to the element in such a way that in can be read later. In this 042 * case the serialized value of the string array would be as follows. 043 * <pre> 044 * 045 * <value>one, two, three</value> 046 * 047 * </pre> 048 * Here each non-null string is inserted in to a comma separated 049 * list of values, which can later be deserialized. Just to note the 050 * above array could be annotated with <code>ElementList</code> just 051 * as easily, in which case each entry would have its own element. 052 * The choice of which annotation to use is up to the developer. A 053 * more obvious benefit to transformations like this can be seen for 054 * values annotated with the <code>Attribute</code> annotation. 055 * 056 * @author Niall Gallagher 057 */ 058public class Transformer { 059 060 /** 061 * This is used to cache all transforms matched to a given type. 062 */ 063 private final Cache<Transform> cache; 064 065 /** 066 * This is used to cache the types that to not have a transform. 067 */ 068 private final Cache<Object> error; 069 070 /** 071 * This is used to perform the matching of types to transforms. 072 */ 073 private final Matcher matcher; 074 075 /** 076 * Constructor for the <code>Transformer</code> object. This is 077 * used to create a transformer which will transform specified 078 * types using transforms loaded from the class path. Transforms 079 * are matched to types using the specified matcher object. 080 * 081 * @param matcher this is used to match types to transforms 082 */ 083 public Transformer(Matcher matcher) { 084 this.cache = new ConcurrentCache<Transform>(); 085 this.error = new ConcurrentCache<Object>(); 086 this.matcher = new DefaultMatcher(matcher); 087 } 088 089 /** 090 * This method is used to convert the string value given to an 091 * appropriate representation. This is used when an object is 092 * being deserialized from the XML document and the value for 093 * the string representation is required. 094 * 095 * @param value this is the string representation of the value 096 * @param type this is the type to convert the string value to 097 * 098 * @return this returns an appropriate instanced to be used 099 */ 100 public Object read(String value, Class type) throws Exception { 101 Transform transform = lookup(type); 102 103 if(transform == null) { 104 throw new TransformException("Transform of %s not supported", type); 105 } 106 return transform.read(value); 107 } 108 109 /** 110 * This method is used to convert the provided value into an XML 111 * usable format. This is used in the serialization process when 112 * there is a need to convert a field value in to a string so 113 * that that value can be written as a valid XML entity. 114 * 115 * @param value this is the value to be converted to a string 116 * @param type this is the type to convert to a string value 117 * 118 * @return this is the string representation of the given value 119 */ 120 public String write(Object value, Class type) throws Exception { 121 Transform transform = lookup(type); 122 123 if(transform == null) { 124 throw new TransformException("Transform of %s not supported", type); 125 } 126 return transform.write(value); 127 } 128 129 /** 130 * This method is used to determine if the type specified can be 131 * transformed. This will use the <code>Matcher</code> to find a 132 * suitable transform, if one exists then this returns true, if 133 * not then this returns false. This is used during serialization 134 * to determine how to convert a field or method parameter. 135 * 136 * @param type the type to determine whether its transformable 137 * 138 * @return true if the type specified can be transformed by this 139 */ 140 public boolean valid(Class type) throws Exception { 141 return lookup(type) != null; 142 } 143 144 /** 145 * This method is used to acquire a <code>Transform</code> for 146 * the the specified type. If there is no transform for the type 147 * then this will return null. Once acquired once the transform 148 * is cached so that subsequent lookups will be performed faster. 149 * 150 * @param type the type to determine whether its transformable 151 * 152 * @return this will return a transform for the specified type 153 */ 154 private Transform lookup(Class type) throws Exception { 155 if(!error.contains(type)) { 156 Transform transform = cache.fetch(type); 157 158 if(transform != null) { 159 return transform; 160 } 161 return match(type); 162 } 163 return null; 164 } 165 166 /** 167 * This method is used to acquire a <code>Transform</code> for 168 * the the specified type. If there is no transform for the type 169 * then this will return null. Once acquired once the transform 170 * is cached so that subsequent lookups will be performed faster. 171 * 172 * @param type the type to determine whether its transformable 173 * 174 * @return this will return a transform for the specified type 175 */ 176 private Transform match(Class type) throws Exception { 177 Transform transform = matcher.match(type); 178 179 if(transform != null) { 180 cache.cache(type, transform); 181 } else { 182 error.cache(type, this); 183 } 184 return transform; 185 } 186}