View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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  
19  package org.eclipse.jetty.util;
20  
21  import java.io.IOException;
22  import java.lang.annotation.Annotation;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.lang.reflect.Modifier;
27  import java.util.Arrays;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Objects;
33  
34  import org.eclipse.jetty.util.annotation.Name;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.util.log.Logger;
37  
38  
39  /* ------------------------------------------------------------ */
40  /**
41   * TYPE Utilities.
42   * Provides various static utiltiy methods for manipulating types and their
43   * string representations.
44   *
45   * @since Jetty 4.1
46   */
47  public class TypeUtil
48  {
49      private static final Logger LOG = Log.getLogger(TypeUtil.class);
50      public static final Class<?>[] NO_ARGS = new Class[]{};
51      public static final int CR = '\015';
52      public static final int LF = '\012';
53  
54      /* ------------------------------------------------------------ */
55      private static final HashMap<String, Class<?>> name2Class=new HashMap<>();
56      static
57      {
58          name2Class.put("boolean",java.lang.Boolean.TYPE);
59          name2Class.put("byte",java.lang.Byte.TYPE);
60          name2Class.put("char",java.lang.Character.TYPE);
61          name2Class.put("double",java.lang.Double.TYPE);
62          name2Class.put("float",java.lang.Float.TYPE);
63          name2Class.put("int",java.lang.Integer.TYPE);
64          name2Class.put("long",java.lang.Long.TYPE);
65          name2Class.put("short",java.lang.Short.TYPE);
66          name2Class.put("void",java.lang.Void.TYPE);
67  
68          name2Class.put("java.lang.Boolean.TYPE",java.lang.Boolean.TYPE);
69          name2Class.put("java.lang.Byte.TYPE",java.lang.Byte.TYPE);
70          name2Class.put("java.lang.Character.TYPE",java.lang.Character.TYPE);
71          name2Class.put("java.lang.Double.TYPE",java.lang.Double.TYPE);
72          name2Class.put("java.lang.Float.TYPE",java.lang.Float.TYPE);
73          name2Class.put("java.lang.Integer.TYPE",java.lang.Integer.TYPE);
74          name2Class.put("java.lang.Long.TYPE",java.lang.Long.TYPE);
75          name2Class.put("java.lang.Short.TYPE",java.lang.Short.TYPE);
76          name2Class.put("java.lang.Void.TYPE",java.lang.Void.TYPE);
77  
78          name2Class.put("java.lang.Boolean",java.lang.Boolean.class);
79          name2Class.put("java.lang.Byte",java.lang.Byte.class);
80          name2Class.put("java.lang.Character",java.lang.Character.class);
81          name2Class.put("java.lang.Double",java.lang.Double.class);
82          name2Class.put("java.lang.Float",java.lang.Float.class);
83          name2Class.put("java.lang.Integer",java.lang.Integer.class);
84          name2Class.put("java.lang.Long",java.lang.Long.class);
85          name2Class.put("java.lang.Short",java.lang.Short.class);
86  
87          name2Class.put("Boolean",java.lang.Boolean.class);
88          name2Class.put("Byte",java.lang.Byte.class);
89          name2Class.put("Character",java.lang.Character.class);
90          name2Class.put("Double",java.lang.Double.class);
91          name2Class.put("Float",java.lang.Float.class);
92          name2Class.put("Integer",java.lang.Integer.class);
93          name2Class.put("Long",java.lang.Long.class);
94          name2Class.put("Short",java.lang.Short.class);
95  
96          name2Class.put(null,java.lang.Void.TYPE);
97          name2Class.put("string",java.lang.String.class);
98          name2Class.put("String",java.lang.String.class);
99          name2Class.put("java.lang.String",java.lang.String.class);
100     }
101 
102     /* ------------------------------------------------------------ */
103     private static final HashMap<Class<?>, String> class2Name=new HashMap<>();
104     static
105     {
106         class2Name.put(java.lang.Boolean.TYPE,"boolean");
107         class2Name.put(java.lang.Byte.TYPE,"byte");
108         class2Name.put(java.lang.Character.TYPE,"char");
109         class2Name.put(java.lang.Double.TYPE,"double");
110         class2Name.put(java.lang.Float.TYPE,"float");
111         class2Name.put(java.lang.Integer.TYPE,"int");
112         class2Name.put(java.lang.Long.TYPE,"long");
113         class2Name.put(java.lang.Short.TYPE,"short");
114         class2Name.put(java.lang.Void.TYPE,"void");
115 
116         class2Name.put(java.lang.Boolean.class,"java.lang.Boolean");
117         class2Name.put(java.lang.Byte.class,"java.lang.Byte");
118         class2Name.put(java.lang.Character.class,"java.lang.Character");
119         class2Name.put(java.lang.Double.class,"java.lang.Double");
120         class2Name.put(java.lang.Float.class,"java.lang.Float");
121         class2Name.put(java.lang.Integer.class,"java.lang.Integer");
122         class2Name.put(java.lang.Long.class,"java.lang.Long");
123         class2Name.put(java.lang.Short.class,"java.lang.Short");
124 
125         class2Name.put(null,"void");
126         class2Name.put(java.lang.String.class,"java.lang.String");
127     }
128 
129     /* ------------------------------------------------------------ */
130     private static final HashMap<Class<?>, Method> class2Value=new HashMap<>();
131     static
132     {
133         try
134         {
135             Class<?>[] s ={java.lang.String.class};
136 
137             class2Value.put(java.lang.Boolean.TYPE,
138                            java.lang.Boolean.class.getMethod("valueOf",s));
139             class2Value.put(java.lang.Byte.TYPE,
140                            java.lang.Byte.class.getMethod("valueOf",s));
141             class2Value.put(java.lang.Double.TYPE,
142                            java.lang.Double.class.getMethod("valueOf",s));
143             class2Value.put(java.lang.Float.TYPE,
144                            java.lang.Float.class.getMethod("valueOf",s));
145             class2Value.put(java.lang.Integer.TYPE,
146                            java.lang.Integer.class.getMethod("valueOf",s));
147             class2Value.put(java.lang.Long.TYPE,
148                            java.lang.Long.class.getMethod("valueOf",s));
149             class2Value.put(java.lang.Short.TYPE,
150                            java.lang.Short.class.getMethod("valueOf",s));
151 
152             class2Value.put(java.lang.Boolean.class,
153                            java.lang.Boolean.class.getMethod("valueOf",s));
154             class2Value.put(java.lang.Byte.class,
155                            java.lang.Byte.class.getMethod("valueOf",s));
156             class2Value.put(java.lang.Double.class,
157                            java.lang.Double.class.getMethod("valueOf",s));
158             class2Value.put(java.lang.Float.class,
159                            java.lang.Float.class.getMethod("valueOf",s));
160             class2Value.put(java.lang.Integer.class,
161                            java.lang.Integer.class.getMethod("valueOf",s));
162             class2Value.put(java.lang.Long.class,
163                            java.lang.Long.class.getMethod("valueOf",s));
164             class2Value.put(java.lang.Short.class,
165                            java.lang.Short.class.getMethod("valueOf",s));
166         }
167         catch(Exception e)
168         {
169             throw new Error(e);
170         }
171     }
172 
173     /* ------------------------------------------------------------ */
174     /** Array to List.
175      * <p>
176      * Works like {@link Arrays#asList(Object...)}, but handles null arrays.
177      * @param a the array to convert to a list
178      * @return a list backed by the array.
179      * @param <T> the array and list entry type
180      */
181     public static <T> List<T> asList(T[] a)
182     {
183         if (a==null)
184             return Collections.emptyList();
185         return Arrays.asList(a);
186     }
187 
188     /* ------------------------------------------------------------ */
189     /** Class from a canonical name for a type.
190      * @param name A class or type name.
191      * @return A class , which may be a primitive TYPE field..
192      */
193     public static Class<?> fromName(String name)
194     {
195         return name2Class.get(name);
196     }
197 
198     /* ------------------------------------------------------------ */
199     /** Canonical name for a type.
200      * @param type A class , which may be a primitive TYPE field.
201      * @return Canonical name.
202      */
203     public static String toName(Class<?> type)
204     {
205         return class2Name.get(type);
206     }
207 
208     /* ------------------------------------------------------------ */
209     /** Convert String value to instance.
210      * @param type The class of the instance, which may be a primitive TYPE field.
211      * @param value The value as a string.
212      * @return The value as an Object.
213      */
214     public static Object valueOf(Class<?> type, String value)
215     {
216         try
217         {
218             if (type.equals(java.lang.String.class))
219                 return value;
220 
221             Method m = class2Value.get(type);
222             if (m!=null)
223                 return m.invoke(null, value);
224 
225             if (type.equals(java.lang.Character.TYPE) ||
226                 type.equals(java.lang.Character.class))
227                 return value.charAt(0);
228 
229             Constructor<?> c = type.getConstructor(java.lang.String.class);
230             return c.newInstance(value);
231         }
232         catch (NoSuchMethodException | IllegalAccessException | InstantiationException x)
233         {
234             LOG.ignore(x);
235         }
236         catch (InvocationTargetException x)
237         {
238             if (x.getTargetException() instanceof Error)
239                 throw (Error)x.getTargetException();
240             LOG.ignore(x);
241         }
242         return null;
243     }
244 
245     /* ------------------------------------------------------------ */
246     /** Convert String value to instance.
247      * @param type classname or type (eg int)
248      * @param value The value as a string.
249      * @return The value as an Object.
250      */
251     public static Object valueOf(String type, String value)
252     {
253         return valueOf(fromName(type),value);
254     }
255 
256     /* ------------------------------------------------------------ */
257     /** Parse an int from a substring.
258      * Negative numbers are not handled.
259      * @param s String
260      * @param offset Offset within string
261      * @param length Length of integer or -1 for remainder of string
262      * @param base base of the integer
263      * @return the parsed integer
264      * @throws NumberFormatException if the string cannot be parsed
265      */
266     public static int parseInt(String s, int offset, int length, int base)
267         throws NumberFormatException
268     {
269         int value=0;
270 
271         if (length<0)
272             length=s.length()-offset;
273 
274         for (int i=0;i<length;i++)
275         {
276             char c=s.charAt(offset+i);
277 
278             int digit=convertHexDigit((int)c);
279             if (digit<0 || digit>=base)
280                 throw new NumberFormatException(s.substring(offset,offset+length));
281             value=value*base+digit;
282         }
283         return value;
284     }
285 
286     /* ------------------------------------------------------------ */
287     /** Parse an int from a byte array of ascii characters.
288      * Negative numbers are not handled.
289      * @param b byte array
290      * @param offset Offset within string
291      * @param length Length of integer or -1 for remainder of string
292      * @param base base of the integer
293      * @return the parsed integer
294      * @throws NumberFormatException if the array cannot be parsed into an integer
295      */
296     public static int parseInt(byte[] b, int offset, int length, int base)
297         throws NumberFormatException
298     {
299         int value=0;
300 
301         if (length<0)
302             length=b.length-offset;
303 
304         for (int i=0;i<length;i++)
305         {
306             char c=(char)(0xff&b[offset+i]);
307 
308             int digit=c-'0';
309             if (digit<0 || digit>=base || digit>=10)
310             {
311                 digit=10+c-'A';
312                 if (digit<10 || digit>=base)
313                     digit=10+c-'a';
314             }
315             if (digit<0 || digit>=base)
316                 throw new NumberFormatException(new String(b,offset,length));
317             value=value*base+digit;
318         }
319         return value;
320     }
321 
322     /* ------------------------------------------------------------ */
323     public static byte[] parseBytes(String s, int base)
324     {
325         byte[] bytes=new byte[s.length()/2];
326         for (int i=0;i<s.length();i+=2)
327             bytes[i/2]=(byte)TypeUtil.parseInt(s,i,2,base);
328         return bytes;
329     }
330 
331     /* ------------------------------------------------------------ */
332     public static String toString(byte[] bytes, int base)
333     {
334         StringBuilder buf = new StringBuilder();
335         for (byte b : bytes)
336         {
337             int bi=0xff&b;
338             int c='0'+(bi/base)%base;
339             if (c>'9')
340                 c= 'a'+(c-'0'-10);
341             buf.append((char)c);
342             c='0'+bi%base;
343             if (c>'9')
344                 c= 'a'+(c-'0'-10);
345             buf.append((char)c);
346         }
347         return buf.toString();
348     }
349 
350     /* ------------------------------------------------------------ */
351     /**
352      * @param c An ASCII encoded character 0-9 a-f A-F
353      * @return The byte value of the character 0-16.
354      */
355     public static byte convertHexDigit( byte c )
356     {
357         byte b = (byte)((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
358         if (b<0 || b>15)
359             throw new NumberFormatException("!hex "+c);
360         return b;
361     }
362     
363     /* ------------------------------------------------------------ */
364     /**
365      * @param c An ASCII encoded character 0-9 a-f A-F
366      * @return The byte value of the character 0-16.
367      */
368     public static int convertHexDigit( char c )
369     {
370         int d= ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
371         if (d<0 || d>15)
372             throw new NumberFormatException("!hex "+c);
373         return d;
374     }
375     
376     /* ------------------------------------------------------------ */
377     /**
378      * @param c An ASCII encoded character 0-9 a-f A-F
379      * @return The byte value of the character 0-16.
380      */
381     public static int convertHexDigit( int c )
382     {
383         int d= ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10);
384         if (d<0 || d>15)
385             throw new NumberFormatException("!hex "+c);
386         return d;
387     }
388 
389     /* ------------------------------------------------------------ */
390     public static void toHex(byte b,Appendable buf)
391     {
392         try
393         {
394             int d=0xf&((0xF0&b)>>4);
395             buf.append((char)((d>9?('A'-10):'0')+d));
396             d=0xf&b;
397             buf.append((char)((d>9?('A'-10):'0')+d));
398         }
399         catch(IOException e)
400         {
401             throw new RuntimeException(e);
402         }
403     }
404 
405     /* ------------------------------------------------------------ */
406     public static void toHex(int value,Appendable buf) throws IOException
407     {
408         int d=0xf&((0xF0000000&value)>>28);
409         buf.append((char)((d>9?('A'-10):'0')+d));
410         d=0xf&((0x0F000000&value)>>24);
411         buf.append((char)((d>9?('A'-10):'0')+d));
412         d=0xf&((0x00F00000&value)>>20);
413         buf.append((char)((d>9?('A'-10):'0')+d));
414         d=0xf&((0x000F0000&value)>>16);
415         buf.append((char)((d>9?('A'-10):'0')+d));
416         d=0xf&((0x0000F000&value)>>12);
417         buf.append((char)((d>9?('A'-10):'0')+d));
418         d=0xf&((0x00000F00&value)>>8);
419         buf.append((char)((d>9?('A'-10):'0')+d));
420         d=0xf&((0x000000F0&value)>>4);
421         buf.append((char)((d>9?('A'-10):'0')+d));
422         d=0xf&value;
423         buf.append((char)((d>9?('A'-10):'0')+d));
424     
425         Integer.toString(0,36);
426     }
427     
428     
429     /* ------------------------------------------------------------ */
430     public static void toHex(long value,Appendable buf) throws IOException
431     {
432         toHex((int)(value>>32),buf);
433         toHex((int)value,buf);
434     }
435 
436     /* ------------------------------------------------------------ */
437     public static String toHexString(byte b)
438     {
439         return toHexString(new byte[]{b}, 0, 1);
440     }
441 
442     /* ------------------------------------------------------------ */
443     public static String toHexString(byte[] b)
444     {
445         return toHexString(b, 0, b.length);
446     }
447 
448     /* ------------------------------------------------------------ */
449     public static String toHexString(byte[] b,int offset,int length)
450     {
451         StringBuilder buf = new StringBuilder();
452         for (int i=offset;i<offset+length;i++)
453         {
454             int bi=0xff&b[i];
455             int c='0'+(bi/16)%16;
456             if (c>'9')
457                 c= 'A'+(c-'0'-10);
458             buf.append((char)c);
459             c='0'+bi%16;
460             if (c>'9')
461                 c= 'a'+(c-'0'-10);
462             buf.append((char)c);
463         }
464         return buf.toString();
465     }
466 
467     /* ------------------------------------------------------------ */
468     public static byte[] fromHexString(String s)
469     {
470         if (s.length()%2!=0)
471             throw new IllegalArgumentException(s);
472         byte[] array = new byte[s.length()/2];
473         for (int i=0;i<array.length;i++)
474         {
475             int b = Integer.parseInt(s.substring(i*2,i*2+2),16);
476             array[i]=(byte)(0xff&b);
477         }
478         return array;
479     }
480 
481 
482     public static void dump(Class<?> c)
483     {
484         System.err.println("Dump: "+c);
485         dump(c.getClassLoader());
486     }
487 
488     public static void dump(ClassLoader cl)
489     {
490         System.err.println("Dump Loaders:");
491         while(cl!=null)
492         {
493             System.err.println("  loader "+cl);
494             cl = cl.getParent();
495         }
496     }
497 
498 
499     public static Object call(Class<?> oClass, String methodName, Object obj, Object[] arg)
500        throws InvocationTargetException, NoSuchMethodException
501     {
502         Objects.requireNonNull(oClass,"Class cannot be null");
503         Objects.requireNonNull(methodName,"Method name cannot be null");
504         if (StringUtil.isBlank(methodName))
505         {
506             throw new IllegalArgumentException("Method name cannot be blank");
507         }
508         
509         // Lets just try all methods for now
510         for (Method method : oClass.getMethods())
511         {
512             if (!method.getName().equals(methodName))
513                 continue;            
514             if (method.getParameterTypes().length != arg.length)
515                 continue;
516             if (Modifier.isStatic(method.getModifiers()) != (obj == null))
517                 continue;
518             if ((obj == null) && method.getDeclaringClass() != oClass)
519                 continue;
520 
521             try
522             {
523                 return method.invoke(obj, arg);
524             }
525             catch (IllegalAccessException | IllegalArgumentException e)
526             {
527                 LOG.ignore(e);
528             }
529         }
530         
531         // Lets look for a method with optional arguments
532         Object[] args_with_opts=null;
533         
534         for (Method method : oClass.getMethods())
535         {
536             if (!method.getName().equals(methodName))
537                 continue;            
538             if (method.getParameterTypes().length != arg.length+1)
539                 continue;
540             if (!method.getParameterTypes()[arg.length].isArray())
541                 continue;
542             if (Modifier.isStatic(method.getModifiers()) != (obj == null))
543                 continue;
544             if ((obj == null) && method.getDeclaringClass() != oClass)
545                 continue;
546 
547             if (args_with_opts==null)
548                 args_with_opts=ArrayUtil.addToArray(arg,new Object[]{},Object.class);
549             try
550             {
551                 return method.invoke(obj, args_with_opts);
552             }
553             catch (IllegalAccessException | IllegalArgumentException e)
554             {
555                 LOG.ignore(e);
556             }
557         }
558         
559         
560         throw new NoSuchMethodException(methodName);
561     }
562 
563     public static Object construct(Class<?> klass, Object[] arguments) throws InvocationTargetException, NoSuchMethodException
564     {
565         Objects.requireNonNull(klass,"Class cannot be null");
566         
567         for (Constructor<?> constructor : klass.getConstructors())
568         {
569             if (arguments == null)
570             {
571                 // null arguments in .newInstance() is allowed
572                 if (constructor.getParameterTypes().length != 0)
573                     continue;
574             }
575             else if (constructor.getParameterTypes().length != arguments.length)
576                 continue;
577 
578             try
579             {
580                 return constructor.newInstance(arguments);
581             }
582             catch (InstantiationException | IllegalAccessException | IllegalArgumentException e)
583             {
584                 LOG.ignore(e);
585             }
586         }
587         throw new NoSuchMethodException("<init>");
588     }
589     
590     public static Object construct(Class<?> klass, Object[] arguments, Map<String, Object> namedArgMap) throws InvocationTargetException, NoSuchMethodException
591     {
592         Objects.requireNonNull(klass,"Class cannot be null");
593         Objects.requireNonNull(namedArgMap,"Named Argument Map cannot be null");
594         
595         for (Constructor<?> constructor : klass.getConstructors())
596         {
597             if (arguments == null)
598             {
599                 // null arguments in .newInstance() is allowed
600                 if (constructor.getParameterTypes().length != 0)
601                     continue;
602             }
603             else if (constructor.getParameterTypes().length != arguments.length)
604                 continue;
605 
606             try
607             {
608                 Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
609                 
610                 if (arguments == null || arguments.length == 0)
611                 {
612                     if (LOG.isDebugEnabled())
613                         LOG.debug("Constructor has no arguments");
614                     return constructor.newInstance(arguments);
615                 }
616                 else if (parameterAnnotations == null || parameterAnnotations.length == 0)
617                 {
618                     if (LOG.isDebugEnabled())
619                         LOG.debug("Constructor has no parameter annotations");
620                     return constructor.newInstance(arguments);
621                 }
622                 else
623                 {
624                    Object[] swizzled = new Object[arguments.length];
625                    
626                    int count = 0;
627                    for ( Annotation[] annotations : parameterAnnotations )
628                    {
629                        for ( Annotation annotation : annotations)
630                        {
631                            if ( annotation instanceof Name )
632                            {
633                                Name param = (Name)annotation;
634                                
635                                if (namedArgMap.containsKey(param.value()))
636                                {
637                                    if (LOG.isDebugEnabled())
638                                        LOG.debug("placing named {} in position {}", param.value(), count);
639                                    swizzled[count] = namedArgMap.get(param.value());
640                                }
641                                else
642                                {
643                                    if (LOG.isDebugEnabled())
644                                        LOG.debug("placing {} in position {}", arguments[count], count);
645                                    swizzled[count] = arguments[count];
646                                }
647                                ++count;
648                            }
649                            else
650                            {
651                                if (LOG.isDebugEnabled())
652                                    LOG.debug("passing on annotation {}", annotation);
653                            }
654                        }
655                    }
656                    
657                    return constructor.newInstance(swizzled);
658                 }
659                 
660             }
661             catch (InstantiationException | IllegalAccessException | IllegalArgumentException e)
662             {
663                 LOG.ignore(e);
664             }
665         }
666         throw new NoSuchMethodException("<init>");
667     }
668 
669     /* ------------------------------------------------------------ */
670     /** 
671      * @param o Object to test for true
672      * @return True if passed object is not null and is either a Boolean with value true or evaluates to a string that evaluates to true.
673      */
674     public static boolean isTrue(Object o)
675     {
676         if (o==null)
677             return false;
678         if (o instanceof Boolean)
679             return ((Boolean)o).booleanValue();
680         return Boolean.parseBoolean(o.toString());
681     }
682 
683     /* ------------------------------------------------------------ */
684     /** 
685      * @param o Object to test for false
686      * @return True if passed object is not null and is either a Boolean with value false or evaluates to a string that evaluates to false.
687      */
688     public static boolean isFalse(Object o)
689     {
690         if (o==null)
691             return false;
692         if (o instanceof Boolean)
693             return !((Boolean)o).booleanValue();
694         return "false".equalsIgnoreCase(o.toString());
695     }
696 }