1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.util;
20
21import java.io.IOException;
22import java.io.InputStream;
23import java.lang.reflect.Constructor;
24import java.lang.reflect.InvocationTargetException;
25import java.lang.reflect.Method;
26import java.lang.reflect.Modifier;
27import java.net.URL;
28import java.util.Arrays;
29import java.util.Collections;
30import java.util.HashMap;
31import java.util.List;
32
33import org.eclipse.jetty.util.log.Log;
34import org.eclipse.jetty.util.log.Logger;
35
36
37/* ------------------------------------------------------------ */
38/**
39 * TYPE Utilities.
40 * Provides various static utiltiy methods for manipulating types and their
41 * string representations.
42 *
43 * @since Jetty 4.1
44 */
45public class TypeUtil
46{
47    private static final Logger LOG = Log.getLogger(TypeUtil.class);
48    public static int CR = '\015';
49    public static int LF = '\012';
50
51    /* ------------------------------------------------------------ */
52    private static final HashMap<String, Class<?>> name2Class=new HashMap<String, Class<?>>();
53    static
54    {
55        name2Class.put("boolean",java.lang.Boolean.TYPE);
56        name2Class.put("byte",java.lang.Byte.TYPE);
57        name2Class.put("char",java.lang.Character.TYPE);
58        name2Class.put("double",java.lang.Double.TYPE);
59        name2Class.put("float",java.lang.Float.TYPE);
60        name2Class.put("int",java.lang.Integer.TYPE);
61        name2Class.put("long",java.lang.Long.TYPE);
62        name2Class.put("short",java.lang.Short.TYPE);
63        name2Class.put("void",java.lang.Void.TYPE);
64
65        name2Class.put("java.lang.Boolean.TYPE",java.lang.Boolean.TYPE);
66        name2Class.put("java.lang.Byte.TYPE",java.lang.Byte.TYPE);
67        name2Class.put("java.lang.Character.TYPE",java.lang.Character.TYPE);
68        name2Class.put("java.lang.Double.TYPE",java.lang.Double.TYPE);
69        name2Class.put("java.lang.Float.TYPE",java.lang.Float.TYPE);
70        name2Class.put("java.lang.Integer.TYPE",java.lang.Integer.TYPE);
71        name2Class.put("java.lang.Long.TYPE",java.lang.Long.TYPE);
72        name2Class.put("java.lang.Short.TYPE",java.lang.Short.TYPE);
73        name2Class.put("java.lang.Void.TYPE",java.lang.Void.TYPE);
74
75        name2Class.put("java.lang.Boolean",java.lang.Boolean.class);
76        name2Class.put("java.lang.Byte",java.lang.Byte.class);
77        name2Class.put("java.lang.Character",java.lang.Character.class);
78        name2Class.put("java.lang.Double",java.lang.Double.class);
79        name2Class.put("java.lang.Float",java.lang.Float.class);
80        name2Class.put("java.lang.Integer",java.lang.Integer.class);
81        name2Class.put("java.lang.Long",java.lang.Long.class);
82        name2Class.put("java.lang.Short",java.lang.Short.class);
83
84        name2Class.put("Boolean",java.lang.Boolean.class);
85        name2Class.put("Byte",java.lang.Byte.class);
86        name2Class.put("Character",java.lang.Character.class);
87        name2Class.put("Double",java.lang.Double.class);
88        name2Class.put("Float",java.lang.Float.class);
89        name2Class.put("Integer",java.lang.Integer.class);
90        name2Class.put("Long",java.lang.Long.class);
91        name2Class.put("Short",java.lang.Short.class);
92
93        name2Class.put(null,java.lang.Void.TYPE);
94        name2Class.put("string",java.lang.String.class);
95        name2Class.put("String",java.lang.String.class);
96        name2Class.put("java.lang.String",java.lang.String.class);
97    }
98
99    /* ------------------------------------------------------------ */
100    private static final HashMap<Class<?>, String> class2Name=new HashMap<Class<?>, String>();
101    static
102    {
103        class2Name.put(java.lang.Boolean.TYPE,"boolean");
104        class2Name.put(java.lang.Byte.TYPE,"byte");
105        class2Name.put(java.lang.Character.TYPE,"char");
106        class2Name.put(java.lang.Double.TYPE,"double");
107        class2Name.put(java.lang.Float.TYPE,"float");
108        class2Name.put(java.lang.Integer.TYPE,"int");
109        class2Name.put(java.lang.Long.TYPE,"long");
110        class2Name.put(java.lang.Short.TYPE,"short");
111        class2Name.put(java.lang.Void.TYPE,"void");
112
113        class2Name.put(java.lang.Boolean.class,"java.lang.Boolean");
114        class2Name.put(java.lang.Byte.class,"java.lang.Byte");
115        class2Name.put(java.lang.Character.class,"java.lang.Character");
116        class2Name.put(java.lang.Double.class,"java.lang.Double");
117        class2Name.put(java.lang.Float.class,"java.lang.Float");
118        class2Name.put(java.lang.Integer.class,"java.lang.Integer");
119        class2Name.put(java.lang.Long.class,"java.lang.Long");
120        class2Name.put(java.lang.Short.class,"java.lang.Short");
121
122        class2Name.put(null,"void");
123        class2Name.put(java.lang.String.class,"java.lang.String");
124    }
125
126    /* ------------------------------------------------------------ */
127    private static final HashMap<Class<?>, Method> class2Value=new HashMap<Class<?>, Method>();
128    static
129    {
130        try
131        {
132            Class<?>[] s ={java.lang.String.class};
133
134            class2Value.put(java.lang.Boolean.TYPE,
135                           java.lang.Boolean.class.getMethod("valueOf",s));
136            class2Value.put(java.lang.Byte.TYPE,
137                           java.lang.Byte.class.getMethod("valueOf",s));
138            class2Value.put(java.lang.Double.TYPE,
139                           java.lang.Double.class.getMethod("valueOf",s));
140            class2Value.put(java.lang.Float.TYPE,
141                           java.lang.Float.class.getMethod("valueOf",s));
142            class2Value.put(java.lang.Integer.TYPE,
143                           java.lang.Integer.class.getMethod("valueOf",s));
144            class2Value.put(java.lang.Long.TYPE,
145                           java.lang.Long.class.getMethod("valueOf",s));
146            class2Value.put(java.lang.Short.TYPE,
147                           java.lang.Short.class.getMethod("valueOf",s));
148
149            class2Value.put(java.lang.Boolean.class,
150                           java.lang.Boolean.class.getMethod("valueOf",s));
151            class2Value.put(java.lang.Byte.class,
152                           java.lang.Byte.class.getMethod("valueOf",s));
153            class2Value.put(java.lang.Double.class,
154                           java.lang.Double.class.getMethod("valueOf",s));
155            class2Value.put(java.lang.Float.class,
156                           java.lang.Float.class.getMethod("valueOf",s));
157            class2Value.put(java.lang.Integer.class,
158                           java.lang.Integer.class.getMethod("valueOf",s));
159            class2Value.put(java.lang.Long.class,
160                           java.lang.Long.class.getMethod("valueOf",s));
161            class2Value.put(java.lang.Short.class,
162                           java.lang.Short.class.getMethod("valueOf",s));
163        }
164        catch(Exception e)
165        {
166            throw new Error(e);
167        }
168    }
169
170    /* ------------------------------------------------------------ */
171    /** Array to List.
172     * <p>
173     * Works like {@link Arrays#asList(Object...)}, but handles null arrays.
174     * @return a list backed by the array.
175     */
176    public static <T> List<T> asList(T[] a)
177    {
178        if (a==null)
179            return Collections.emptyList();
180        return Arrays.asList(a);
181    }
182
183    /* ------------------------------------------------------------ */
184    /** Class from a canonical name for a type.
185     * @param name A class or type name.
186     * @return A class , which may be a primitive TYPE field..
187     */
188    public static Class<?> fromName(String name)
189    {
190        return name2Class.get(name);
191    }
192
193    /* ------------------------------------------------------------ */
194    /** Canonical name for a type.
195     * @param type A class , which may be a primitive TYPE field.
196     * @return Canonical name.
197     */
198    public static String toName(Class<?> type)
199    {
200        return class2Name.get(type);
201    }
202
203    /* ------------------------------------------------------------ */
204    /** Convert String value to instance.
205     * @param type The class of the instance, which may be a primitive TYPE field.
206     * @param value The value as a string.
207     * @return The value as an Object.
208     */
209    public static Object valueOf(Class<?> type, String value)
210    {
211        try
212        {
213            if (type.equals(java.lang.String.class))
214                return value;
215
216            Method m = class2Value.get(type);
217            if (m!=null)
218                return m.invoke(null, value);
219
220            if (type.equals(java.lang.Character.TYPE) ||
221                type.equals(java.lang.Character.class))
222                return new Character(value.charAt(0));
223
224            Constructor<?> c = type.getConstructor(java.lang.String.class);
225            return c.newInstance(value);
226        }
227        catch(NoSuchMethodException e)
228        {
229            // LogSupport.ignore(log,e);
230        }
231        catch(IllegalAccessException e)
232        {
233            // LogSupport.ignore(log,e);
234        }
235        catch(InstantiationException e)
236        {
237            // LogSupport.ignore(log,e);
238        }
239        catch(InvocationTargetException e)
240        {
241            if (e.getTargetException() instanceof Error)
242                throw (Error)(e.getTargetException());
243            // LogSupport.ignore(log,e);
244        }
245        return null;
246    }
247
248    /* ------------------------------------------------------------ */
249    /** Convert String value to instance.
250     * @param type classname or type (eg int)
251     * @param value The value as a string.
252     * @return The value as an Object.
253     */
254    public static Object valueOf(String type, String value)
255    {
256        return valueOf(fromName(type),value);
257    }
258
259    /* ------------------------------------------------------------ */
260    /** Parse an int from a substring.
261     * Negative numbers are not handled.
262     * @param s String
263     * @param offset Offset within string
264     * @param length Length of integer or -1 for remainder of string
265     * @param base base of the integer
266     * @return the parsed integer
267     * @throws NumberFormatException if the string cannot be parsed
268     */
269    public static int parseInt(String s, int offset, int length, int base)
270        throws NumberFormatException
271    {
272        int value=0;
273
274        if (length<0)
275            length=s.length()-offset;
276
277        for (int i=0;i<length;i++)
278        {
279            char c=s.charAt(offset+i);
280
281            int digit=convertHexDigit((int)c);
282            if (digit<0 || digit>=base)
283                throw new NumberFormatException(s.substring(offset,offset+length));
284            value=value*base+digit;
285        }
286        return value;
287    }
288
289    /* ------------------------------------------------------------ */
290    /** Parse an int from a byte array of ascii characters.
291     * Negative numbers are not handled.
292     * @param b byte array
293     * @param offset Offset within string
294     * @param length Length of integer or -1 for remainder of string
295     * @param base base of the integer
296     * @return the parsed integer
297     * @throws NumberFormatException if the array cannot be parsed into an integer
298     */
299    public static int parseInt(byte[] b, int offset, int length, int base)
300        throws NumberFormatException
301    {
302        int value=0;
303
304        if (length<0)
305            length=b.length-offset;
306
307        for (int i=0;i<length;i++)
308        {
309            char c=(char)(0xff&b[offset+i]);
310
311            int digit=c-'0';
312            if (digit<0 || digit>=base || digit>=10)
313            {
314                digit=10+c-'A';
315                if (digit<10 || digit>=base)
316                    digit=10+c-'a';
317            }
318            if (digit<0 || digit>=base)
319                throw new NumberFormatException(new String(b,offset,length));
320            value=value*base+digit;
321        }
322        return value;
323    }
324
325    /* ------------------------------------------------------------ */
326    public static byte[] parseBytes(String s, int base)
327    {
328        byte[] bytes=new byte[s.length()/2];
329        for (int i=0;i<s.length();i+=2)
330            bytes[i/2]=(byte)TypeUtil.parseInt(s,i,2,base);
331        return bytes;
332    }
333
334    /* ------------------------------------------------------------ */
335    public static String toString(byte[] bytes, int base)
336    {
337        StringBuilder buf = new StringBuilder();
338        for (byte b : bytes)
339        {
340            int bi=0xff&b;
341            int c='0'+(bi/base)%base;
342            if (c>'9')
343                c= 'a'+(c-'0'-10);
344            buf.append((char)c);
345            c='0'+bi%base;
346            if (c>'9')
347                c= 'a'+(c-'0'-10);
348            buf.append((char)c);
349        }
350        return buf.toString();
351    }
352
353    /* ------------------------------------------------------------ */
354    /**
355     * @param c An ASCII encoded character 0-9 a-f A-F
356     * @return The byte value of the character 0-16.
357     */
358    public static byte convertHexDigit( byte c )
359    {
360        byte b = (byte)((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
361        if (b<0 || b>15)
362            throw new IllegalArgumentException("!hex "+c);
363        return b;
364    }
365
366    /* ------------------------------------------------------------ */
367    /**
368     * @param c An ASCII encoded character 0-9 a-f A-F
369     * @return The byte value of the character 0-16.
370     */
371    public static int convertHexDigit( int c )
372    {
373        int d= ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
374        if (d<0 || d>15)
375            throw new NumberFormatException("!hex "+c);
376        return d;
377    }
378
379    /* ------------------------------------------------------------ */
380    public static void toHex(byte b,Appendable buf)
381    {
382        try
383        {
384            int d=0xf&((0xF0&b)>>4);
385            buf.append((char)((d>9?('A'-10):'0')+d));
386            d=0xf&b;
387            buf.append((char)((d>9?('A'-10):'0')+d));
388        }
389        catch(IOException e)
390        {
391            throw new RuntimeException(e);
392        }
393    }
394
395    /* ------------------------------------------------------------ */
396    public static void toHex(int value,Appendable buf) throws IOException
397    {
398        int d=0xf&((0xF0000000&value)>>28);
399        buf.append((char)((d>9?('A'-10):'0')+d));
400        d=0xf&((0x0F000000&value)>>24);
401        buf.append((char)((d>9?('A'-10):'0')+d));
402        d=0xf&((0x00F00000&value)>>20);
403        buf.append((char)((d>9?('A'-10):'0')+d));
404        d=0xf&((0x000F0000&value)>>16);
405        buf.append((char)((d>9?('A'-10):'0')+d));
406        d=0xf&((0x0000F000&value)>>12);
407        buf.append((char)((d>9?('A'-10):'0')+d));
408        d=0xf&((0x00000F00&value)>>8);
409        buf.append((char)((d>9?('A'-10):'0')+d));
410        d=0xf&((0x000000F0&value)>>4);
411        buf.append((char)((d>9?('A'-10):'0')+d));
412        d=0xf&value;
413        buf.append((char)((d>9?('A'-10):'0')+d));
414
415        Integer.toString(0,36);
416    }
417
418
419    /* ------------------------------------------------------------ */
420    public static void toHex(long value,Appendable buf) throws IOException
421    {
422        toHex((int)(value>>32),buf);
423        toHex((int)value,buf);
424    }
425
426    /* ------------------------------------------------------------ */
427    public static String toHexString(byte b)
428    {
429        return toHexString(new byte[]{b}, 0, 1);
430    }
431
432    /* ------------------------------------------------------------ */
433    public static String toHexString(byte[] b)
434    {
435        return toHexString(b, 0, b.length);
436    }
437
438    /* ------------------------------------------------------------ */
439    public static String toHexString(byte[] b,int offset,int length)
440    {
441        StringBuilder buf = new StringBuilder();
442        for (int i=offset;i<offset+length;i++)
443        {
444            int bi=0xff&b[i];
445            int c='0'+(bi/16)%16;
446            if (c>'9')
447                c= 'A'+(c-'0'-10);
448            buf.append((char)c);
449            c='0'+bi%16;
450            if (c>'9')
451                c= 'a'+(c-'0'-10);
452            buf.append((char)c);
453        }
454        return buf.toString();
455    }
456
457    /* ------------------------------------------------------------ */
458    public static byte[] fromHexString(String s)
459    {
460        if (s.length()%2!=0)
461            throw new IllegalArgumentException(s);
462        byte[] array = new byte[s.length()/2];
463        for (int i=0;i<array.length;i++)
464        {
465            int b = Integer.parseInt(s.substring(i*2,i*2+2),16);
466            array[i]=(byte)(0xff&b);
467        }
468        return array;
469    }
470
471
472    public static void dump(Class<?> c)
473    {
474        System.err.println("Dump: "+c);
475        dump(c.getClassLoader());
476    }
477
478    public static void dump(ClassLoader cl)
479    {
480        System.err.println("Dump Loaders:");
481        while(cl!=null)
482        {
483            System.err.println("  loader "+cl);
484            cl = cl.getParent();
485        }
486    }
487
488
489    /* ------------------------------------------------------------ */
490    /**
491     * @deprecated
492     */
493    public static byte[] readLine(InputStream in) throws IOException
494    {
495        byte[] buf = new byte[256];
496
497        int i=0;
498        int loops=0;
499        int ch=0;
500
501        while (true)
502        {
503            ch=in.read();
504            if (ch<0)
505                break;
506            loops++;
507
508            // skip a leading LF's
509            if (loops==1 && ch==LF)
510                continue;
511
512            if (ch==CR || ch==LF)
513                break;
514
515            if (i>=buf.length)
516            {
517                byte[] old_buf=buf;
518                buf=new byte[old_buf.length+256];
519                System.arraycopy(old_buf, 0, buf, 0, old_buf.length);
520            }
521            buf[i++]=(byte)ch;
522        }
523
524        if (ch==-1 && i==0)
525            return null;
526
527        // skip a trailing LF if it exists
528        if (ch==CR && in.available()>=1 && in.markSupported())
529        {
530            in.mark(1);
531            ch=in.read();
532            if (ch!=LF)
533                in.reset();
534        }
535
536        byte[] old_buf=buf;
537        buf=new byte[i];
538        System.arraycopy(old_buf, 0, buf, 0, i);
539
540        return buf;
541    }
542
543    public static URL jarFor(String className)
544    {
545        try
546        {
547            className=className.replace('.','/')+".class";
548            // hack to discover jstl libraries
549            URL url = Loader.getResource(null,className,false);
550            String s=url.toString();
551            if (s.startsWith("jar:file:"))
552                return new URL(s.substring(4,s.indexOf("!/")));
553        }
554        catch(Exception e)
555        {
556            LOG.ignore(e);
557        }
558        return null;
559    }
560
561    public static Object call(Class<?> oClass, String method, Object obj, Object[] arg)
562       throws InvocationTargetException, NoSuchMethodException
563    {
564        // Lets just try all methods for now
565        Method[] methods = oClass.getMethods();
566        for (int c = 0; methods != null && c < methods.length; c++)
567        {
568            if (!methods[c].getName().equals(method))
569                continue;
570            if (methods[c].getParameterTypes().length != arg.length)
571                continue;
572            if (Modifier.isStatic(methods[c].getModifiers()) != (obj == null))
573                continue;
574            if ((obj == null) && methods[c].getDeclaringClass() != oClass)
575                continue;
576
577            try
578            {
579                return methods[c].invoke(obj,arg);
580            }
581            catch (IllegalAccessException e)
582            {
583                LOG.ignore(e);
584            }
585            catch (IllegalArgumentException e)
586            {
587                LOG.ignore(e);
588            }
589        }
590
591        throw new NoSuchMethodException(method);
592    }
593}
594