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 }