001    package railo.commons.lang.mimetype;
002    
003    import java.util.Arrays;
004    import java.util.HashMap;
005    import java.util.Map;
006    
007    import railo.commons.lang.StringUtil;
008    import railo.runtime.op.Caster;
009    import railo.runtime.type.UDF;
010    import railo.runtime.type.util.ListUtil;
011    
012    public class MimeType {
013            
014            private static int DEFAULT_MXB=100000;
015            private static double DEFAULT_MXT=5;
016            private static double DEFAULT_QUALITY=1;
017            
018            public static final MimeType ALL = new MimeType(null,null,null);
019            public static final MimeType APPLICATION_JSON = new MimeType("application","json",null);
020            public static final MimeType APPLICATION_XML = new MimeType("application","xml",null);
021            public static final MimeType APPLICATION_WDDX = new MimeType("application","wddx",null);
022            public static final MimeType APPLICATION_CFML = new MimeType("application","cfml",null);
023            public static final MimeType APPLICATION_PLAIN = new MimeType("application","lazy",null);
024    
025            public static final MimeType IMAGE_GIF = new MimeType("image","gif",null);
026            public static final MimeType IMAGE_JPG = new MimeType("image","jpeg",null);
027            public static final MimeType IMAGE_PNG = new MimeType("image","png",null);
028            public static final MimeType IMAGE_TIFF = new MimeType("image","tiff",null);
029            public static final MimeType IMAGE_BMP = new MimeType("image","bmp",null);
030            public static final MimeType IMAGE_WBMP = new MimeType("image","vnd.wap.wbmp",null);
031            public static final MimeType IMAGE_FBX = new MimeType("image","fbx",null);
032            public static final MimeType IMAGE_PNM = new MimeType("image","x-portable-anymap",null);
033            public static final MimeType IMAGE_PGM = new MimeType("image","x-portable-graymap",null);
034            public static final MimeType IMAGE_PBM = new MimeType("image","x-portable-bitmap",null);
035            public static final MimeType IMAGE_ICO = new MimeType("image","ico",null);
036            public static final MimeType IMAGE_PSD = new MimeType("image","psd",null);
037            public static final MimeType IMAGE_ASTERIX = new MimeType("image",null,null);
038            public static final MimeType APPLICATION_JAVA = new MimeType("application","java",null);
039            
040            private String type;
041            private String subtype;
042            //private double quality;
043            //private int mxb;
044            //private double mxt;
045            private Map<String,String> properties;
046            private double q=-1;
047    
048            
049            private MimeType(String type, String subtype, Map<String,String> properties) {
050                    //if(quality<0 || quality>1)
051                    //      throw new RuntimeException("quality must be a number between 0 and 1, now ["+quality+"]");
052    
053                    
054                    this.type=type;
055                    this.subtype=subtype;
056                    this.properties=properties;
057                    //this.quality=quality;
058                    //this.mxb=mxb;
059                    //this.mxt=mxt;
060            }
061            
062    
063    
064            private static MimeType getInstance(String type, String subtype, Map<String,String> properties) {
065                    // TODO read this from a external File
066                    if("text".equals(type)) {
067                            if("xml".equals(subtype)) return new MimeType("application", "xml", properties);
068                            if("x-json".equals(subtype)) return new MimeType("application", "json", properties);
069                            if("javascript".equals(subtype)) return new MimeType("application", "json", properties);
070                            if("x-javascript".equals(subtype)) return new MimeType("application", "json", properties);
071                            if("wddx".equals(subtype)) return new MimeType("application", "wddx", properties);
072                    }
073                    else if("application".equals(type)) {
074                            if("x-json".equals(subtype)) return new MimeType("application", "json", properties);
075                            if("javascript".equals(subtype)) return new MimeType("application", "json", properties);
076                            if("x-javascript".equals(subtype)) return new MimeType("application", "json", properties);
077                            
078                            if("jpg".equals(subtype)) return new MimeType("image", "jpeg", properties);
079                            if("x-jpg".equals(subtype)) return new MimeType("image", "jpeg", properties);
080                            
081                            if("png".equals(subtype)) return new MimeType("image", "png", properties);
082                            if("x-png".equals(subtype)) return new MimeType("image", "png", properties);
083    
084                            if("tiff".equals(subtype)) return new MimeType("image", "tiff", properties);
085                            if("tif".equals(subtype)) return new MimeType("image", "tiff", properties);
086                            if("x-tiff".equals(subtype)) return new MimeType("image", "tiff", properties);
087                            if("x-tif".equals(subtype)) return new MimeType("image", "tiff", properties);
088    
089                            if("fpx".equals(subtype)) return new MimeType("image", "fpx", properties);
090                            if("x-fpx".equals(subtype)) return new MimeType("image", "fpx", properties);
091                            if("vnd.fpx".equals(subtype)) return new MimeType("image", "fpx", properties);
092                            if("vnd.netfpx".equals(subtype)) return new MimeType("image", "fpx", properties);
093                            
094                            if("ico".equals(subtype)) return new MimeType("image", "ico", properties);
095                            if("x-ico".equals(subtype)) return new MimeType("image", "ico", properties);
096                            if("x-icon".equals(subtype)) return new MimeType("image", "ico", properties);
097                            
098                            if("psd".equals(subtype)) return new MimeType("image", "psd", properties);
099                            if("x-photoshop".equals(subtype)) return new MimeType("image", "psd", properties);
100                            if("photoshop".equals(subtype)) return new MimeType("image", "psd", properties);
101                    }
102                    else if("image".equals(type)) {
103                            if("gi_".equals(subtype)) return new MimeType("image", "gif", properties);
104                            
105                            if("pjpeg".equals(subtype)) return new MimeType("image", "jpeg", properties);
106                            if("jpg".equals(subtype)) return new MimeType("image", "jpeg", properties);
107                            if("jpe".equals(subtype)) return new MimeType("image", "jpeg", properties);
108                            if("vnd.swiftview-jpeg".equals(subtype)) return new MimeType("image", "jpeg", properties);
109                            if("pipeg".equals(subtype)) return new MimeType("image", "jpeg", properties);
110                            if("jp_".equals(subtype)) return new MimeType("image", "jpeg", properties);
111    
112                            if("x-png".equals(subtype)) return new MimeType("image", "png", properties);
113    
114                            if("tif".equals(subtype)) return new MimeType("image", "tiff", properties);
115                            if("x-tif".equals(subtype)) return new MimeType("image", "tiff", properties);
116                            if("x-tiff".equals(subtype)) return new MimeType("image", "tiff", properties);
117    
118                            if("x-fpx".equals(subtype)) return new MimeType("image", "fpx", properties);
119                            if("vnd.fpx".equals(subtype)) return new MimeType("image", "fpx", properties);
120                            if("vnd.netfpx".equals(subtype)) return new MimeType("image", "fpx", properties);
121    
122                            if("x-portable/graymap".equals(subtype)) return new MimeType("image", "x-portable-anymap", properties);
123                            if("portable graymap".equals(subtype)) return new MimeType("image", "x-portable-anymap", properties);
124                            if("x-pnm".equals(subtype)) return new MimeType("image", "x-portable-anymap", properties);
125                            if("pnm".equals(subtype)) return new MimeType("image", "x-portable-anymap", properties);
126    
127                            if("x-portable/graymap".equals(subtype)) return new MimeType("image", "x-portable-anymap", properties);
128                            if("portable graymap".equals(subtype)) return new MimeType("image", "x-portable-anymap", properties);
129                            if("x-pgm".equals(subtype)) return new MimeType("image", "x-portable-anymap", properties);
130                            if("pgm".equals(subtype)) return new MimeType("image", "x-portable-graymap", properties);
131    
132                            if("portable bitmap".equals(subtype)) return new MimeType("image", "x-portable-bitmap", properties);
133                            if("x-portable/bitmap".equals(subtype)) return new MimeType("image", "x-portable-bitmap", properties);
134                            if("x-pbm".equals(subtype)) return new MimeType("image", "x-portable-bitmap", properties);
135                            if("pbm".equals(subtype)) return new MimeType("image", "x-portable-bitmap", properties);
136    
137                            if("x-ico".equals(subtype)) return new MimeType("image", "ico", properties);
138                            if("x-icon".equals(subtype)) return new MimeType("image", "ico", properties);
139    
140                            if("x-photoshop".equals(subtype)) return new MimeType("image", "psd", properties);
141                            if("photoshop".equals(subtype)) return new MimeType("image", "psd", properties);
142                    }
143                    else if("zz-application".equals(type)) {
144                            if("zz-winassoc-psd".equals(subtype)) return new MimeType("image", "psd", properties);
145                    }
146                    /*
147                    
148                    if("image/x-p".equals(mt)) return "ppm";
149                    if("image/x-ppm".equals(mt)) return "ppm";
150                    if("image/ppm".equals(mt)) return "ppm";
151    
152                    */
153                    return new MimeType(type, subtype, properties);
154            }
155    
156            
157            /**
158             * returns a mimetype that match given string
159             * @param strMimeType
160             * @return
161             */
162            public static MimeType getInstance(String strMimeType){
163                    if(strMimeType==null) return ALL;
164                    strMimeType=strMimeType.trim();
165                    if("*".equals(strMimeType) || strMimeType.length()==0) return ALL;
166                    
167                    String[] arr = ListUtil.listToStringArray(strMimeType, ';');
168                    
169                    String[] arrCT = ListUtil.listToStringArray(arr[0].trim(), '/');
170                    
171                    String type=arrCT[0].trim();
172                    if("*".equals(type)) type=null;
173                    
174                    if(arrCT.length==1) return getInstance(type,null,null);
175                    
176                    String subtype=arrCT[1].trim();
177                    if("*".equals(subtype)) subtype=null;
178                    
179                    if(arr.length==1) return getInstance(type,subtype,null);
180                    
181    
182                    final Map<String,String> properties=new HashMap<String, String>();
183                    String entry;
184                    String[] _arr;
185                    for(int i=1;i<arr.length;i++){
186                            entry=arr[i].trim();
187                            _arr = ListUtil.listToStringArray(entry, '=');
188                            if(arr.length<2) continue;
189                            properties.put(_arr[0].trim().toLowerCase(), _arr[1].trim());
190                            //if(_arr[0].equals("q")) quality=Caster.toDoubleValue(_arr[1],1);
191                            //if(_arr[0].equals("mxb")) mxb=Caster.toIntValue(_arr[1],100000);
192                            //if(_arr[0].equals("mxt")) mxt=Caster.toDoubleValue(_arr[1],5);
193                    }
194                    return getInstance(type,subtype,properties);
195            }
196    
197            public static MimeType[] getInstances(String strMimeTypes, char delimiter) {
198                    if(StringUtil.isEmpty(strMimeTypes,true)) return new MimeType[0];
199                    String[] arr = ListUtil.trimItems(ListUtil.listToStringArray(strMimeTypes, delimiter));
200                    MimeType[] mtes=new MimeType[arr.length];
201                    for(int i=0;i<arr.length;i++){
202                            mtes[i]=getInstance(arr[i]);
203                    }
204                    return mtes;
205            }
206    
207    
208    
209    
210            /**
211             * @return the type
212             */
213            public String getType() {
214                    return type;
215            }
216    
217            /**
218             * @return the subtype
219             */
220            public String getSubtype() {
221                    return subtype;
222            }
223            
224            /**
225             * @return the type
226             */
227            String getTypeNotNull() {
228                    return type==null?"*":type;
229            }
230    
231            /**
232             * @return the subtype
233             */
234            String getSubtypeNotNull() {
235                    return subtype==null?"*":subtype;
236            }
237            
238    
239            public double getQuality() {
240                    if(q==-1){
241                            if(properties==null) q=DEFAULT_QUALITY;
242                            else q= Caster.toDoubleValue(properties.get("q"),DEFAULT_QUALITY);
243                    }
244                    return q;
245            }
246            /*
247            public int getMxb() {
248                    return Caster.toIntValue(properties.get("mxb"),DEFAULT_MXB);
249            }
250    
251            public double getMxt() {
252                    return Caster.toDoubleValue(properties.get("mxt"),DEFAULT_MXT);
253            }*/
254    
255            public boolean hasWildCards() {
256                    return type==null || subtype==null;
257            }
258    
259            /**
260             * checks if given mimetype is covered by current mimetype
261             * @param other
262             * @return
263             */
264            public boolean match(MimeType other){
265                    if(this==other) return true;
266                    if(type!=null && other.type!=null && !type.equals(other.type)) return false;
267                    if(subtype!=null && other.subtype!=null && !subtype.equals(other.subtype)) return false;
268                    return true;
269            }
270            
271            public MimeType bestMatch(MimeType[] others){
272                    MimeType best=null;
273                    
274                    for(int i=0;i<others.length;i++){
275                            if(match(others[i]) && (best==null || best.getQuality()<others[i].getQuality())) {
276                                    best=others[i];
277                            }
278                    }
279                    return best;
280            }
281            
282    
283    
284            /**
285             * checks if other is from the same type, just type and subtype are checked, properties (q,mxb,mxt) are ignored.
286             * @param other
287             * @return
288             */
289            public boolean same(MimeType other){
290                    if(this==other) return true;
291                    return getTypeNotNull().equals(other.getTypeNotNull()) && getSubtypeNotNull().equals(other.getSubtypeNotNull());
292            }
293            
294            public boolean equals(Object obj){
295                    if(obj==this) return true;
296                    
297                    
298                    MimeType other;
299                    if(obj instanceof MimeType)
300                            other=(MimeType) obj;
301                    else if(obj instanceof String)
302                            other=MimeType.getInstance((String)obj);
303                    else
304                            return false;
305                    
306                    if(!same(other)) return false;
307                    
308                    return other.toString().equals(toString());
309            }
310            
311            public String toString(){
312                    StringBuilder sb=new StringBuilder();
313                    sb.append(type==null?"*":type);
314                    sb.append("/");
315                    sb.append(subtype==null?"*":subtype);
316                    if(properties!=null){
317                            String[] keys = properties.keySet().toArray(new String[properties.size()]);
318                            Arrays.sort(keys);
319                            //Iterator<Entry<String, String>> it = properties.entrySet().iterator();
320                            //Entry<String, String> e;
321                            for(int i=0;i<keys.length;i++){
322                                    sb.append("; ");
323                                    sb.append(keys[i]);
324                                    sb.append("=");
325                                    sb.append(properties.get(keys[i]));
326                            }
327                    }
328                    return sb.toString();
329            }
330    
331    
332    
333            public static MimeType toMimetype(int format, MimeType defaultValue) {
334                    switch(format){
335                    case UDF.RETURN_FORMAT_JSON:return MimeType.APPLICATION_JSON;
336                    case UDF.RETURN_FORMAT_WDDX:return MimeType.APPLICATION_WDDX;
337                    case UDF.RETURN_FORMAT_SERIALIZE:return MimeType.APPLICATION_CFML;
338                    case UDF.RETURN_FORMAT_XML:return MimeType.APPLICATION_XML;
339                    case UDF.RETURN_FORMAT_PLAIN:return MimeType.APPLICATION_PLAIN;
340                    
341                    }
342                    return defaultValue;
343            }
344            
345            public static int toFormat(MimeType mt, int defaultValue) {
346                    if(mt==null) return defaultValue;
347                    if(MimeType.APPLICATION_JSON.same(mt)) return  UDF.RETURN_FORMAT_JSON;
348                    if(MimeType.APPLICATION_WDDX.same(mt)) return  UDF.RETURN_FORMAT_WDDX;
349                    if(MimeType.APPLICATION_CFML.same(mt)) return  UDF.RETURN_FORMAT_SERIALIZE;
350                    if(MimeType.APPLICATION_XML.same(mt)) return  UDF.RETURN_FORMAT_XML;
351                    if(MimeType.APPLICATION_PLAIN.same(mt)) return  UDF.RETURN_FORMAT_PLAIN;
352                    return defaultValue;
353            }
354    }