View Javadoc
1   package org.djutils.reflection;
2   
3   import java.io.Serializable;
4   import java.lang.reflect.Array;
5   import java.util.LinkedHashMap;
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-2022 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 = 20191230L;
26  
27      /** the CAHCHe. */
28      private static final Map<String, Class<?>> CACHE = new LinkedHashMap<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          this.value = value;
40      }
41  
42      /**
43       * constructs a new FieldSignature.
44       * @param clazz Class&lt;?&gt;; The class
45       */
46      public FieldSignature(final Class<?> clazz)
47      {
48          this(FieldSignature.toDescriptor(clazz));
49      }
50  
51      /**
52       * @return Returns the value of the field descriptor
53       */
54      public String getStringValue()
55      {
56          return this.value;
57      }
58  
59      /**
60       * @return Returns the value of the field descriptor
61       * @throws ClassNotFoundException if the class cannot be found.
62       */
63      public Class<?> getClassValue() throws ClassNotFoundException
64      {
65          return FieldSignature.toClass(this.value);
66      }
67  
68      /** {@inheritDoc} */
69      @Override
70      public String toString()
71      {
72          return this.value;
73      }
74  
75      /**
76       * converts an array of fields to its descriptor.
77       * @param classes Class&lt;?&gt;[]; the classes to represent
78       * @return String the descriptor String
79       */
80      public static final String toDescriptor(final Class<?>[] classes)
81      {
82          StringBuilder result = new StringBuilder();
83          for (int i = 0; i < classes.length; i++)
84          {
85              result.append(FieldSignature.toDescriptor(classes[i]));
86          }
87          return result.toString();
88      }
89  
90      /**
91       * converts a field to its descriptor.
92       * @param clazz Class&lt;?&gt;; the clazz to represent
93       * @return String the descriptor String
94       */
95      public static final String toDescriptor(final Class<?> clazz)
96      {
97          if (clazz.getName().startsWith("["))
98          {
99              return clazz.getName().replace('.', '/');
100         }
101         if (clazz.isPrimitive())
102         {
103             if (clazz.equals(int.class))
104             {
105                 return "I";
106             }
107             if (clazz.equals(double.class))
108             {
109                 return "D";
110             }
111             if (clazz.equals(boolean.class))
112             {
113                 return "Z";
114             }
115             if (clazz.equals(char.class))
116             {
117                 return "C";
118             }
119             if (clazz.equals(byte.class))
120             {
121                 return "B";
122             }
123             if (clazz.equals(float.class))
124             {
125                 return "F";
126             }
127             if (clazz.equals(long.class))
128             {
129                 return "J";
130             }
131             if (clazz.equals(short.class))
132             {
133                 return "S";
134             }
135             return "V";
136         }
137         return "L" + clazz.getName().replace('.', '/') + ";";
138     }
139 
140     /**
141      * converts a fieldDescriptor to its class representation.
142      * @param descriptor String; the descriptor
143      * @return Class the class
144      * @throws ClassNotFoundException on failure
145      */
146     public static final Class<?> toClass(final String descriptor) throws ClassNotFoundException
147     {
148         if (FieldSignature.CACHE.containsKey(descriptor))
149         {
150             return FieldSignature.CACHE.get(descriptor);
151         }
152         String className = descriptor;
153         Class<?> result = null;
154         int array = 0;
155         while (className.charAt(array) == '[')
156         {
157             array++;
158         }
159         className = className.substring(array);
160         if (className.startsWith("L"))
161         {
162             className = className.replaceAll("/", ".");
163             className = className.substring(1, className.length() - 1);
164             try
165             {
166                 result = Class.forName(className);
167             }
168             catch (Exception exception)
169             {
170                 result = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
171             }
172         }
173         else
174         {
175             result = Primitive.forName(className);
176         }
177         if (result == null && !descriptor.startsWith("["))
178         {
179             // For some reason not all classes start with L and end with ;
180             return FieldSignature.toClass("L" + descriptor + ";");
181         }
182         if (array == 0)
183         {
184             FieldSignature.CACHE.put(descriptor, result);
185             return result;
186         }
187         try
188         {
189             int[] dimensions = new int[array];
190             result = Array.newInstance(result, dimensions).getClass();
191         }
192         catch (Exception exception)
193         {
194             throw new ClassNotFoundException(result + " class not found");
195         }
196         FieldSignature.CACHE.put(descriptor, result);
197         return result;
198     }
199 }