001    // Copyright 2007, 2008, 2010 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.tapestry5;
016    
017    import java.util.List;
018    import java.util.Map;
019    import java.util.StringTokenizer;
020    
021    import org.apache.tapestry5.internal.InternalConstants;
022    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
023    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
024    
025    /**
026     * Represents an HTTP content type. Allows to set various elements like the mime type, the character set, and other
027     * parameters. This is similar to a number of other implementations of the same concept in JAF, etc. We have created
028     * this simple implementation to avoid including the whole libraries.
029     */
030    public final class ContentType
031    {
032        private String baseType = "";
033    
034        private String subType = "";
035    
036        private final Map<String, String> parameters = CollectionFactory.newCaseInsensitiveMap();
037    
038        /**
039         * Creates a new empty content type.
040         */
041        public ContentType()
042        {
043        }
044    
045        /**
046         * Creates a new content type from the argument. The format of the argument has to be basetype/subtype(;key=value)*
047         *
048         * @param contentType the content type that needs to be represented
049         */
050        public ContentType(String contentType)
051        {
052            parse(contentType);
053        }
054    
055        /**
056         * Creates a new content type with the given MIME type and charset
057         */
058        public ContentType(String contentType, String charset)
059        {
060            this(contentType);
061    
062            setParameter(InternalConstants.CHARSET_CONTENT_TYPE_PARAMETER, charset);
063        }
064    
065    
066        /**
067         * Returns true only if the other object is another instance of ContentType, and has the ssame baseType, subType and
068         * set of parameters.
069         */
070        @Override
071        public boolean equals(Object o)
072        {
073            if (o == null) return false;
074    
075            if (o.getClass() != this.getClass()) return false;
076    
077            ContentType ct = (ContentType) o;
078    
079            return baseType.equals(ct.baseType) && subType.equals(ct.subType) && parameters.equals(ct.parameters);
080        }
081    
082        /**
083         * @return the base type of the content type
084         */
085        public String getBaseType()
086        {
087            return baseType;
088        }
089    
090        /**
091         * @param baseType
092         */
093        public void setBaseType(String baseType)
094        {
095            assert baseType != null;
096            this.baseType = baseType;
097        }
098    
099        /**
100         * @return the sub-type of the content type
101         */
102        public String getSubType()
103        {
104            return subType;
105        }
106    
107        /**
108         * @param subType
109         */
110        public void setSubType(String subType)
111        {
112            assert subType != null;
113            this.subType = subType;
114        }
115    
116        /**
117         * @return the MIME type of the content type
118         */
119        public String getMimeType()
120        {
121            return baseType + "/" + subType;
122        }
123    
124        /**
125         * @return the list of names of parameters in this content type, in alphabetical order.
126         */
127        public List<String> getParameterNames()
128        {
129            return InternalUtils.sortedKeys(parameters);
130        }
131    
132        /**
133         * @return the character set (the  "charset" parameter) or null.
134         */
135        public String getCharset()
136        {
137            return getParameter(InternalConstants.CHARSET_CONTENT_TYPE_PARAMETER);
138        }
139    
140        /**
141         * @param key the name of the content type parameter
142         * @return the value of the content type parameter
143         */
144        public String getParameter(String key)
145        {
146            assert key != null;
147            return parameters.get(key);
148        }
149    
150        /**
151         * @param key   the name of the content type parameter
152         * @param value the value of the content type parameter
153         */
154        public void setParameter(String key, String value)
155        {
156            assert key != null;
157            assert value != null;
158            parameters.put(key, value);
159        }
160    
161        /**
162         * Parses the argument and configures the content type accordingly. The format of the argument has to be
163         * type/subtype(;key=value)*
164         *
165         * @param contentType the content type that needs to be represented
166         */
167        public void parse(String contentType)
168        {
169            baseType = "";
170            subType = "";
171            parameters.clear();
172    
173            StringTokenizer tokens = new StringTokenizer(contentType, ";");
174            if (!tokens.hasMoreTokens()) return;
175    
176            String mimeType = tokens.nextToken();
177            StringTokenizer mimeTokens = new StringTokenizer(mimeType, "/");
178            setBaseType(mimeTokens.hasMoreTokens() ? mimeTokens.nextToken() : "");
179            setSubType(mimeTokens.hasMoreTokens() ? mimeTokens.nextToken() : "");
180    
181            while (tokens.hasMoreTokens())
182            {
183                String parameter = tokens.nextToken();
184    
185                StringTokenizer parameterTokens = new StringTokenizer(parameter, "=");
186                String key = parameterTokens.hasMoreTokens() ? parameterTokens.nextToken() : "";
187                String value = parameterTokens.hasMoreTokens() ? parameterTokens.nextToken() : "";
188                setParameter(key, value);
189            }
190        }
191    
192        /**
193         * @return the string representation of this content type
194         */
195        public String unparse()
196        {
197            StringBuilder buffer = new StringBuilder(getMimeType());
198    
199            for (String parameterName : getParameterNames())
200            {
201                buffer.append(";");
202                buffer.append(parameterName);
203                buffer.append("=");
204                buffer.append(parameters.get(parameterName));
205            }
206    
207            return buffer.toString();
208        }
209    
210        /**
211         * @return the string representation of this content type. Same as unparse().
212         */
213        @Override
214        public String toString()
215        {
216            return unparse();
217        }
218    }