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    }