View Javadoc
1   package org.djutils.reflection;
2   
3   import java.io.Serializable;
4   import java.lang.reflect.Array;
5   import java.util.HashMap;
6   import java.util.Map;
7   
8   import org.djutils.primitives.Primitive;
9   
10  /**
11   * A field descriptor represents the type of a class, instance, or local variable. It is a series of characters generated by the
12   * grammar described at <a href = "https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html"> The Java Virtual Machine
13   * class File Format </a>.
14   * <p>
15   * Copyright (c) 2002-2019 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
16   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
17   * distributed under a three-clause BSD-style license, which can be found at
18   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
19   * </p>
20   * @author Peter Jacobs, Niels Lang, Alexander Verbraeck
21   */
22  public class FieldSignature implements Serializable
23  {
24      /** The default serial version UID for serializable classes. */
25      private static final long serialVersionUID = 1L;
26  
27      /** the CAHCHe. */
28      private static final Map<String, Class<?>> CACHE = new HashMap<String, Class<?>>();
29  
30      /** the value of the field descriptor. */
31      private String value;
32  
33      /**
34       * constructs a new FieldSignature.
35       * @param value String; the value of the descriptor
36       */
37      public FieldSignature(final String value)
38      {
39          super();
40          this.value = value;
41      }
42  
43      /**
44       * constructs a new FieldSignature.
45       * @param clazz Class&lt;?&gt;; The class
46       */
47      public FieldSignature(final Class<?> clazz)
48      {
49          this(FieldSignature.toDescriptor(clazz));
50      }
51  
52      /**
53       * @return Returns the value of the field descriptor
54       */
55      public String getStringValue()
56      {
57          return this.value;
58      }
59  
60      /**
61       * @return Returns the value of the field descriptor
62       * @throws ClassNotFoundException if the class cannot be found.
63       */
64      public Class<?> getClassValue() throws ClassNotFoundException
65      {
66          return FieldSignature.toClass(this.value);
67      }
68  
69      /** {@inheritDoc} */
70      @Override
71      public String toString()
72      {
73          return this.value;
74      }
75  
76      /**
77       * converts an array of fields to its descriptor
78       * @param classes Class&lt;?&gt;[]; the classes to represent
79       * @return String the descriptor String
80       */
81      public static final String toDescriptor(final Class<?>[] classes)
82      {
83          String result = "";
84          for (int i = 0; i < classes.length; i++)
85          {
86              result = result + FieldSignature.toDescriptor(classes[i]);
87          }
88          return result;
89      }
90  
91      /**
92       * converts a field to its descriptor
93       * @param clazz Class&lt;?&gt;; the clazz to represent
94       * @return String the descriptor String
95       */
96      public static final String toDescriptor(final Class<?> clazz)
97      {
98          if (clazz.getName().startsWith("["))
99          {
100             return clazz.getName().replace('.', '/');
101         }
102         if (clazz.isPrimitive())
103         {
104             if (clazz.equals(int.class))
105             {
106                 return "I";
107             }
108             if (clazz.equals(double.class))
109             {
110                 return "D";
111             }
112             if (clazz.equals(boolean.class))
113             {
114                 return "Z";
115             }
116             if (clazz.equals(char.class))
117             {
118                 return "C";
119             }
120             if (clazz.equals(byte.class))
121             {
122                 return "B";
123             }
124             if (clazz.equals(float.class))
125             {
126                 return "F";
127             }
128             if (clazz.equals(long.class))
129             {
130                 return "J";
131             }
132             if (clazz.equals(short.class))
133             {
134                 return "S";
135             }
136             return "V";
137         }
138         return "L" + clazz.getName().replace('.', '/') + ";";
139     }
140 
141     /**
142      * converts a fieldDescriptor to its class representation
143      * @param descriptor String; the descriptor
144      * @return Class the class
145      * @throws ClassNotFoundException on failure
146      */
147     public static final Class<?> toClass(final String descriptor) throws ClassNotFoundException
148     {
149         if (FieldSignature.CACHE.containsKey(descriptor))
150         {
151             return FieldSignature.CACHE.get(descriptor);
152         }
153         String className = descriptor;
154         Class<?> result = null;
155         int array = 0;
156         while (className.charAt(array) == '[')
157         {
158             array++;
159         }
160         className = className.substring(array);
161         if (className.startsWith("L"))
162         {
163             className = className.replaceAll("/", ".");
164             className = className.substring(1, className.length() - 1);
165             try
166             {
167                 result = Class.forName(className);
168             }
169             catch (Exception exception)
170             {
171                 result = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
172             }
173         }
174         else
175         {
176             result = Primitive.forName(className);
177         }
178         if (result == null && !descriptor.startsWith("["))
179         {
180             // For some reason not all classes start with L and end with ;
181             return FieldSignature.toClass("L" + descriptor + ";");
182         }
183         if (array == 0)
184         {
185             FieldSignature.CACHE.put(descriptor, result);
186             return result;
187         }
188         try
189         {
190             int[] dimensions = new int[array];
191             result = Array.newInstance(result, dimensions).getClass();
192         }
193         catch (Exception exception)
194         {
195             throw new ClassNotFoundException(result + " class not found");
196         }
197         FieldSignature.CACHE.put(descriptor, result);
198         return result;
199     }
200 }