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