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<?>; 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<?>[]; 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<?>; 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 }