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 *    &#64;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 *    &lt;value&gt;one, two, three&lt;/value&gt;
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}