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 015package org.apache.tapestry.util.io; 016 017import java.util.Iterator; 018import java.util.List; 019 020import org.apache.hivemind.lib.util.StrategyRegistry; 021import org.apache.hivemind.lib.util.StrategyRegistryImpl; 022import org.apache.tapestry.Tapestry; 023import org.apache.tapestry.services.DataSqueezer; 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 032public class DataSqueezerImpl implements DataSqueezer 033{ 034 private static final String NULL_PREFIX = "X"; 035 036 private static final int ARRAY_SIZE = 90; 037 038 private 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 private SqueezeAdaptor[] _adaptorByPrefix = new SqueezeAdaptor[ARRAY_SIZE]; 047 048 /** 049 * AdaptorRegistry cache of adaptors. 050 */ 051 052 private 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 164 int offset = string.charAt(0) - FIRST_ADAPTOR_OFFSET; 165 166 if (offset >= 0 && offset < _adaptorByPrefix.length) 167 adaptor = _adaptorByPrefix[offset]; 168 169 // If the adaptor is not otherwise recognized, the it is simply 170 // an encoded String (the StringAdaptor may not have added 171 // a prefix). 172 173 if (adaptor == null) 174 return string; 175 176 // Adaptor should never be null, because we always supply 177 // an adaptor for String 178 179 return adaptor.unsqueeze(this, string); 180 } 181 182 /** 183 * Convienience method for unsqueezing many strings (back into objects). 184 * <p> 185 * If strings is null, returns null. 186 */ 187 188 public Object[] unsqueeze(String[] strings) 189 { 190 if (strings == null) 191 return null; 192 193 int length = strings.length; 194 Object[] result; 195 196 result = new Object[length]; 197 198 for (int i = 0; i < length; i++) 199 result[i] = unsqueeze(strings[i]); 200 201 return result; 202 } 203 204 public String toString() 205 { 206 StringBuffer buffer; 207 208 buffer = new StringBuffer(); 209 buffer.append("DataSqueezer[adaptors=<"); 210 buffer.append(_adaptors.toString()); 211 buffer.append(">]"); 212 213 return buffer.toString(); 214 } 215}