Marking of more classes, might get rid of in the future and optimize; Implement shrin...
[SquirrelJME.git] / modules / tool-classfile / src / main / java / net / multiphasicapps / classfile / FieldDescriptor.java
blob908007ec36e59e6d47d849238d744db75013bc7b
1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the GNU General Public License v3+, or later.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package net.multiphasicapps.classfile;
12 import cc.squirreljme.runtime.cldc.annotation.Exported;
13 import cc.squirreljme.runtime.cldc.debug.Debugging;
15 /**
16 * This represents the type descriptor of a field.
18 * @since 2017/06/12
20 @Exported
21 public final class FieldDescriptor
22 implements Comparable<FieldDescriptor>, MemberDescriptor
24 /** The short type. */
25 @Exported
26 public static final FieldDescriptor SHORT =
27 new FieldDescriptor("S");
29 /** The integer type. */
30 @Exported
31 public static final FieldDescriptor INTEGER =
32 new FieldDescriptor("I");
34 /** String representation. */
35 protected final String string;
37 /** Is this a primitive type? */
38 protected final boolean primitive;
40 /** Array dimensions. */
41 protected final int dimensions;
43 /** The component type. */
44 protected final FieldDescriptor component;
46 /** The class this refers to. */
47 protected final ClassName classname;
49 /**
50 * Initializes the field descriptor.
52 * @param __n The field descriptor to decode.
53 * @throws InvalidClassFormatException If it is not a valid field descriptor.
54 * @throws NullPointerException On null arguments.
55 * @since 2017/06/12
57 @Exported
58 public FieldDescriptor(String __n)
59 throws InvalidClassFormatException, NullPointerException
61 // Check
62 if (__n == null)
63 throw new NullPointerException("NARG");
65 // Set
66 this.string = __n;
68 // {@squirreljme.error JC2q The field descriptor cannot be blank. (The
69 // field descriptor)}
70 int n = __n.length();
71 if (n <= 0)
72 throw new InvalidClassFormatException(
73 String.format("JC2q %s", __n));
75 // Depends on the first character
76 char c = __n.charAt(0);
77 switch (c)
79 // Primitive
80 case 'B':
81 case 'C':
82 case 'D':
83 case 'F':
84 case 'I':
85 case 'J':
86 case 'S':
87 case 'Z':
88 this.primitive = true;
89 this.dimensions = 0;
90 this.component = null;
91 this.classname = null;
92 break;
94 // Array
95 case '[':
96 this.primitive = false;
97 this.classname = null;
99 // Count dimensions
100 int dims = 0;
101 for (int i = 0; i < n; i++)
102 if (__n.charAt(i) != '[')
103 break;
104 else
105 dims++;
106 this.dimensions = dims;
108 // Parse component
109 this.component = new FieldDescriptor(__n.substring(1));
110 break;
112 // Class
113 case 'L':
114 this.primitive = false;
115 this.dimensions = 0;
116 this.component = null;
118 // {@squirreljme.error JC2r The field descriptor for a class
119 // must end with a semicolon. (The field descriptor)}
120 if (';' != __n.charAt(n - 1))
121 throw new InvalidClassFormatException(
122 String.format("JC2r %s", __n));
124 // Decode
125 this.classname = new ClassName(__n.substring(1, n - 1));
126 break;
128 // {@squirreljme.error JC2s The field descriptor is not valid.
129 // (The field descriptor)}
130 default:
131 throw new InvalidClassFormatException(
132 String.format("JC2s %s", __n));
137 * Adds dimensions to the field descriptor.
139 * @param __d The number of dimensions to add.
140 * @return The field descriptor with added dimensions.
141 * @throws IllegalArgumentException If the dimensions are negative.
142 * @since 2018/09/15
144 @Exported
145 public final FieldDescriptor addDimensions(int __d)
146 throws IllegalArgumentException
148 if (__d == 0)
149 return this;
151 // {@squirreljme.error JC2t Cannot add negative dimensions.}
152 if (__d < 0)
153 throw new IllegalArgumentException("JC2t");
155 // Prepend string with brackets, to declare a new array
156 StringBuilder sb = new StringBuilder();
157 for (int i = 0; i < __d; i++)
158 sb.append('[');
160 // Rebuild field
161 sb.append(this.toString());
162 return new FieldDescriptor(sb.toString());
166 * Returns the name of the used class.
168 * @return The used class or {@code null} if a class is not referred to and
169 * this is a primitive type.
170 * @since 2018/09/01
172 @Exported
173 public final ClassName className()
175 // If this is an array then the class name will be the array descriptor
176 if (this.dimensions > 0)
177 return new ClassName(this.toString());
179 // Otherwise as normal class (or primitive representation)
180 if (this.primitive)
181 return ClassName.fromPrimitiveType(this.primitiveType());
182 return this.classname;
186 * {@inheritDoc}
187 * @since 2017/10/02
189 @Override
190 public int compareTo(FieldDescriptor __o)
192 return this.string.compareTo(__o.string);
196 * Returns the component type of the array if this is one.
198 * @return The component type or {@code null} if this is not one.
199 * @since 2018/09/27
201 @Exported
202 public final FieldDescriptor componentType()
204 return this.component;
208 * Returns the number of dimensions in this class.
210 * @return The number of dimensions in the class.
211 * @since 2018/09/28
213 @Exported
214 public final int dimensions()
216 return this.dimensions;
220 * {@inheritDoc}
221 * @since 2017/06/12
223 @Override
224 public boolean equals(Object __o)
226 if (this == __o)
227 return true;
229 // Check
230 if (!(__o instanceof FieldDescriptor))
231 return false;
233 return this.string.equals(((FieldDescriptor)__o).string);
237 * {@inheritDoc}
238 * @since 2017/06/12
240 @Override
241 public int hashCode()
243 return this.string.hashCode();
247 * Is this an array type?
249 * @return {@code true} if an array type.
250 * @since 2017/10/08
252 @Exported
253 public boolean isArray()
255 return this.component != null;
259 * Is this a primitive type?
261 * @return {@code true} if this is a primitive type.
262 * @since 2017/07/28
264 @Exported
265 public boolean isPrimitive()
267 return this.primitive;
271 * Is this an object type?
273 * @return If this is an object type.
274 * @since 2017/09/16
276 @Exported
277 public boolean isObject()
279 return !this.isPrimitive();
283 * Returns if this is a wide field or not.
285 * @return If this is a wide field.
286 * @since 2019/02/05
288 @Exported
289 public final boolean isWide()
291 if (this.isObject())
292 return false;
293 switch (this.primitiveType())
295 case LONG:
296 case DOUBLE:
297 return true;
299 default:
300 return false;
305 * Returns the primitive type for this field.
307 * @return The primitive type to use or {@code null} if there is none.
308 * @since 2017/10/16
310 @Exported
311 public PrimitiveType primitiveType()
313 // Quick detect
314 if (!this.primitive)
315 return null;
317 // Depends on the string
318 switch (this.toString())
320 case "B": return PrimitiveType.BYTE;
321 case "C": return PrimitiveType.CHARACTER;
322 case "D": return PrimitiveType.DOUBLE;
323 case "F": return PrimitiveType.FLOAT;
324 case "I": return PrimitiveType.INTEGER;
325 case "J": return PrimitiveType.LONG;
326 case "S": return PrimitiveType.SHORT;
327 case "Z": return PrimitiveType.BOOLEAN;
328 default:
329 return null;
334 * Returns the simple storage type of the field.
336 * @return The simple storage type for this field.
337 * @since 2018/09/15
339 @Exported
340 public final SimpleStorageType simpleStorageType()
342 // Objects
343 if (this.isObject())
344 return SimpleStorageType.OBJECT;
346 // Primitive types, these are promoted
347 switch (this.primitiveType())
349 case BOOLEAN:
350 case BYTE:
351 case SHORT:
352 case CHARACTER:
353 case INTEGER:
354 return SimpleStorageType.INTEGER;
356 case LONG:
357 return SimpleStorageType.LONG;
359 case FLOAT:
360 return SimpleStorageType.FLOAT;
362 case DOUBLE:
363 return SimpleStorageType.DOUBLE;
365 // Should not occur
366 default:
367 throw Debugging.oops();
372 * Returns the width of this field on the stack.
374 * @return The width of the field on the stack.
375 * @since 2019/02/05
377 @Exported
378 public final int stackWidth()
380 return (this.isWide() ? 2 : 1);
384 * {@inheritDoc}
385 * @since 2017/06/12
387 @Override
388 public String toString()
390 return this.string;