001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry.util.io;
016
017 import org.apache.hivemind.lib.util.StrategyRegistry;
018 import org.apache.hivemind.lib.util.StrategyRegistryImpl;
019 import org.apache.tapestry.Tapestry;
020 import org.apache.tapestry.services.DataSqueezer;
021
022 import java.util.Iterator;
023 import java.util.List;
024
025 /**
026 * A class used to convert arbitrary objects to Strings and back. This has particular uses involving
027 * HTTP URLs and Cookies.
028 *
029 * @author Howard Lewis Ship
030 */
031
032 public class DataSqueezerImpl implements DataSqueezer
033 {
034 protected static final String NULL_PREFIX = "X";
035
036 protected static final int ARRAY_SIZE = 90;
037
038 protected static final int FIRST_ADAPTOR_OFFSET = 33;
039
040 /**
041 * An array of adaptors; this is used as a cheap lookup-table when unsqueezing. Each adaptor is
042 * identified by a single ASCII character, in the range of 33 ('!') to 122 (the letter 'z'). The
043 * offset into this table is the character minus 33.
044 */
045
046 protected SqueezeAdaptor[] _adaptorByPrefix = new SqueezeAdaptor[ARRAY_SIZE];
047
048 /**
049 * AdaptorRegistry cache of adaptors.
050 */
051
052 protected StrategyRegistry _adaptors = new StrategyRegistryImpl();
053
054 public void setSqueezeAdaptors(List adaptors)
055 {
056 Iterator i = adaptors.iterator();
057
058 while (i.hasNext())
059 {
060 SqueezeAdaptor adaptor = (SqueezeAdaptor) i.next();
061 register(adaptor);
062 }
063 }
064
065 /**
066 * Registers the adaptor with one or more single-character prefixes.
067 * <p>
068 * <b>Note</b>: This method should be used for testing purposes only! Squeeze adaptors are
069 * normally injected by HiveMind.
070 *
071 * @param adaptor
072 * the adaptor which to be registered.
073 */
074
075 public synchronized void register(SqueezeAdaptor adaptor)
076 {
077 if (adaptor == null)
078 throw new IllegalArgumentException(Tapestry.getMessage("DataSqueezer.null-adaptor"));
079
080 String prefix = adaptor.getPrefix();
081 int prefixLength = prefix.length();
082 int offset;
083
084 if (prefixLength < 1)
085 throw new IllegalArgumentException(Tapestry.getMessage("DataSqueezer.short-prefix"));
086
087 Class dataClass = adaptor.getDataClass();
088 if (dataClass == null)
089 throw new IllegalArgumentException(Tapestry.getMessage("DataSqueezer.null-class"));
090
091 for (int i = 0; i < prefixLength; i++)
092 {
093 char ch = prefix.charAt(i);
094
095 if (ch < '!' | ch > 'z')
096 throw new IllegalArgumentException(Tapestry
097 .getMessage("DataSqueezer.prefix-out-of-range"));
098
099 offset = ch - FIRST_ADAPTOR_OFFSET;
100
101 if (_adaptorByPrefix[offset] != null)
102 throw new IllegalArgumentException(Tapestry.format(
103 "DataSqueezer.adaptor-prefix-taken",
104 prefix.substring(i, i)));
105
106 _adaptorByPrefix[offset] = adaptor;
107
108 }
109
110 _adaptors.register(dataClass, adaptor);
111 }
112
113 /**
114 * Squeezes the data object into a String by locating an appropriate adaptor that can perform
115 * the conversion. data may be null.
116 */
117
118 public String squeeze(Object data)
119 {
120 SqueezeAdaptor adaptor;
121
122 if (data == null)
123 return NULL_PREFIX;
124
125 adaptor = (SqueezeAdaptor) _adaptors.getStrategy(data.getClass());
126
127 return adaptor.squeeze(this, data);
128 }
129
130 /**
131 * A convience; invokes {@link #squeeze(Object)}for each element in the data array. If data is
132 * null, returns null.
133 */
134
135 public String[] squeeze(Object[] data)
136 {
137 if (data == null)
138 return null;
139
140 int length = data.length;
141 String[] result;
142
143 result = new String[length];
144
145 for (int i = 0; i < length; i++)
146 result[i] = squeeze(data[i]);
147
148 return result;
149 }
150
151 /**
152 * Unsqueezes the string. Note that in a special case, where the first character of the string
153 * is not a recognized prefix, it is assumed that the string is simply a string, and return with
154 * no change.
155 */
156
157 public Object unsqueeze(String string)
158 {
159 SqueezeAdaptor adaptor = null;
160
161 if (string.equals(NULL_PREFIX))
162 return null;
163 else if (string.length() <= 0)
164 return null;
165
166 int offset = string.charAt(0) - FIRST_ADAPTOR_OFFSET;
167
168 if (offset >= 0 && offset < _adaptorByPrefix.length)
169 adaptor = _adaptorByPrefix[offset];
170
171 // If the adaptor is not otherwise recognized, the it is simply
172 // an encoded String (the StringAdaptor may not have added
173 // a prefix).
174
175 if (adaptor == null)
176 return string;
177
178 // Adaptor should never be null, because we always supply
179 // an adaptor for String
180
181 return adaptor.unsqueeze(this, string);
182 }
183
184 /**
185 * Convienience method for unsqueezing many strings (back into objects).
186 * <p>
187 * If strings is null, returns null.
188 */
189
190 public Object[] unsqueeze(String[] strings)
191 {
192 if (strings == null)
193 return null;
194
195 int length = strings.length;
196 Object[] result;
197
198 result = new Object[length];
199
200 for (int i = 0; i < length; i++)
201 result[i] = unsqueeze(strings[i]);
202
203 return result;
204 }
205
206 public String toString()
207 {
208 StringBuffer buffer;
209
210 buffer = new StringBuffer();
211 buffer.append("DataSqueezer[adaptors=<");
212 buffer.append(_adaptors.toString());
213 buffer.append(">]");
214
215 return buffer.toString();
216 }
217 }