94 return o; |
94 return o; |
95 } |
95 } |
96 } |
96 } |
97 |
97 |
98 final Object[] objects; |
98 final Object[] objects; |
99 protected final List<ValueDescriptor> descriptors; |
99 final ObjectContext objectContext; |
100 protected final TimeConverter timeConverter; |
|
101 |
100 |
102 // package private, not to be subclassed outside this package |
101 // package private, not to be subclassed outside this package |
103 RecordedObject(List<ValueDescriptor> descriptors, Object[] objects, TimeConverter timeConverter) { |
102 RecordedObject(ObjectContext objectContext, Object[] objects) { |
104 this.descriptors = descriptors; |
103 this.objectContext = objectContext; |
105 this.objects = objects; |
104 this.objects = objects; |
106 this.timeConverter = timeConverter; |
|
107 } |
105 } |
108 |
106 |
109 // package private |
107 // package private |
110 final <T> T getTyped(String name, Class<T> clazz, T defaultValue) { |
108 final <T> T getTyped(String name, Class<T> clazz, T defaultValue) { |
111 // Unnecessary to check field presence twice, but this |
109 // Unnecessary to check field presence twice, but this |
131 * |
129 * |
132 * @see #getFields() |
130 * @see #getFields() |
133 */ |
131 */ |
134 public boolean hasField(String name) { |
132 public boolean hasField(String name) { |
135 Objects.requireNonNull(name); |
133 Objects.requireNonNull(name); |
136 for (ValueDescriptor v : descriptors) { |
134 for (ValueDescriptor v : objectContext.fields) { |
137 if (v.getName().equals(name)) { |
135 if (v.getName().equals(name)) { |
138 return true; |
136 return true; |
139 } |
137 } |
140 } |
138 } |
141 int dotIndex = name.indexOf("."); |
139 int dotIndex = name.indexOf("."); |
142 if (dotIndex > 0) { |
140 if (dotIndex > 0) { |
143 String structName = name.substring(0, dotIndex); |
141 String structName = name.substring(0, dotIndex); |
144 for (ValueDescriptor v : descriptors) { |
142 for (ValueDescriptor v : objectContext.fields) { |
145 if (!v.getFields().isEmpty() && v.getName().equals(structName)) { |
143 if (!v.getFields().isEmpty() && v.getName().equals(structName)) { |
146 RecordedObject child = getValue(structName); |
144 RecordedObject child = getValue(structName); |
147 if (child != null) { |
145 if (child != null) { |
148 return child.hasField(name.substring(dotIndex + 1)); |
146 return child.hasField(name.substring(dotIndex + 1)); |
149 } |
147 } |
206 } |
204 } |
207 |
205 |
208 private Object getValue(String name, boolean allowUnsigned) { |
206 private Object getValue(String name, boolean allowUnsigned) { |
209 Objects.requireNonNull(name); |
207 Objects.requireNonNull(name); |
210 int index = 0; |
208 int index = 0; |
211 for (ValueDescriptor v : descriptors) { |
209 for (ValueDescriptor v : objectContext.fields) { |
212 if (name.equals(v.getName())) { |
210 if (name.equals(v.getName())) { |
213 Object object = objectAt(index); |
211 Object object = objectAt(index); |
214 if (object == null) { |
212 if (object == null) { |
215 // error or missing |
213 // error or missing |
216 return null; |
214 return null; |
234 if (v.isArray()) { |
232 if (v.isArray()) { |
235 // struct array |
233 // struct array |
236 return structifyArray(v, array, 0); |
234 return structifyArray(v, array, 0); |
237 } |
235 } |
238 // struct |
236 // struct |
239 return new RecordedObject(v.getFields(), (Object[]) object, timeConverter); |
237 return new RecordedObject(objectContext.getInstance(v), (Object[]) object); |
240 } |
238 } |
241 } |
239 } |
242 index++; |
240 index++; |
243 } |
241 } |
244 |
242 |
245 int dotIndex = name.indexOf("."); |
243 int dotIndex = name.indexOf("."); |
246 if (dotIndex > 0) { |
244 if (dotIndex > 0) { |
247 String structName = name.substring(0, dotIndex); |
245 String structName = name.substring(0, dotIndex); |
248 for (ValueDescriptor v : descriptors) { |
246 for (ValueDescriptor v : objectContext.fields) { |
249 if (!v.getFields().isEmpty() && v.getName().equals(structName)) { |
247 if (!v.getFields().isEmpty() && v.getName().equals(structName)) { |
250 RecordedObject child = getValue(structName); |
248 RecordedObject child = getValue(structName); |
251 String subName = name.substring(dotIndex + 1); |
249 String subName = name.substring(dotIndex + 1); |
252 if (child != null) { |
250 if (child != null) { |
253 return child.getValue(subName, allowUnsigned); |
251 return child.getValue(subName, allowUnsigned); |
295 // This is to prevent a call to getString on a thread field, that is |
293 // This is to prevent a call to getString on a thread field, that is |
296 // null to succeed. |
294 // null to succeed. |
297 private <T> T getTypedValue(String name, String typeName) { |
295 private <T> T getTypedValue(String name, String typeName) { |
298 Objects.requireNonNull(name); |
296 Objects.requireNonNull(name); |
299 // Validate name and type first |
297 // Validate name and type first |
300 getValueDescriptor(descriptors, name, typeName); |
298 getValueDescriptor(objectContext.fields, name, typeName); |
301 return getValue(name); |
299 return getValue(name); |
302 } |
300 } |
303 |
301 |
304 private Object[] structifyArray(ValueDescriptor v, Object[] array, int dimension) { |
302 private Object[] structifyArray(ValueDescriptor v, Object[] array, int dimension) { |
305 if (array == null) { |
303 if (array == null) { |
306 return null; |
304 return null; |
307 } |
305 } |
308 Object[] structArray = new Object[array.length]; |
306 Object[] structArray = new Object[array.length]; |
|
307 ObjectContext objContext = objectContext.getInstance(v); |
309 for (int i = 0; i < structArray.length; i++) { |
308 for (int i = 0; i < structArray.length; i++) { |
310 Object arrayElement = array[i]; |
309 Object arrayElement = array[i]; |
311 if (dimension == 0) { |
310 if (dimension == 0) { |
312 // No general way to handle structarrays |
311 // No general way to handle structarrays |
313 // without invoking ObjectFactory for every instance (which may require id) |
312 // without invoking ObjectFactory for every instance (which may require id) |
314 if (isStackFrameType(v.getTypeName())) { |
313 if (isStackFrameType(v.getTypeName())) { |
315 structArray[i] = new RecordedFrame(v.getFields(), (Object[]) arrayElement, timeConverter); |
314 structArray[i] = new RecordedFrame(objContext, (Object[]) arrayElement); |
316 } else { |
315 } else { |
317 structArray[i] = new RecordedObject(v.getFields(), (Object[]) arrayElement, timeConverter); |
316 structArray[i] = new RecordedObject(objContext, (Object[]) arrayElement); |
318 } |
317 } |
319 } else { |
318 } else { |
320 structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1); |
319 structArray[i] = structifyArray(v, (Object[]) arrayElement, dimension - 1); |
321 } |
320 } |
322 } |
321 } |
337 * Returns an immutable list of the fields for this object. |
336 * Returns an immutable list of the fields for this object. |
338 * |
337 * |
339 * @return the fields, not {@code null} |
338 * @return the fields, not {@code null} |
340 */ |
339 */ |
341 public List<ValueDescriptor> getFields() { |
340 public List<ValueDescriptor> getFields() { |
342 return descriptors; |
341 return objectContext.fields; |
343 } |
342 } |
344 |
343 |
345 /** |
344 /** |
346 * Returns the value of a field of type {@code boolean}. |
345 * Returns the value of a field of type {@code boolean}. |
347 * <p> |
346 * <p> |
759 } |
758 } |
760 throw newIllegalArgumentException(name, "java,time.Duration"); |
759 throw newIllegalArgumentException(name, "java,time.Duration"); |
761 } |
760 } |
762 |
761 |
763 private Duration getDuration(long timespan, String name) throws InternalError { |
762 private Duration getDuration(long timespan, String name) throws InternalError { |
764 ValueDescriptor v = getValueDescriptor(descriptors, name, null); |
763 ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null); |
765 if (timespan == Long.MIN_VALUE) { |
764 if (timespan == Long.MIN_VALUE) { |
766 return Duration.ofSeconds(Long.MIN_VALUE, 0); |
765 return Duration.ofSeconds(Long.MIN_VALUE, 0); |
767 } |
766 } |
768 Timespan ts = v.getAnnotation(Timespan.class); |
767 Timespan ts = v.getAnnotation(Timespan.class); |
769 if (ts != null) { |
768 if (ts != null) { |
775 case Timespan.MILLISECONDS: |
774 case Timespan.MILLISECONDS: |
776 return Duration.ofMillis(timespan); |
775 return Duration.ofMillis(timespan); |
777 case Timespan.NANOSECONDS: |
776 case Timespan.NANOSECONDS: |
778 return Duration.ofNanos(timespan); |
777 return Duration.ofNanos(timespan); |
779 case Timespan.TICKS: |
778 case Timespan.TICKS: |
780 return Duration.ofNanos(timeConverter.convertTimespan(timespan)); |
779 return Duration.ofNanos(objectContext.timeConverter.convertTimespan(timespan)); |
781 } |
780 } |
782 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value()); |
781 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timespan unit " + ts.value()); |
783 } |
782 } |
784 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timespan"); |
783 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timespan"); |
785 } |
784 } |
838 } |
837 } |
839 throw newIllegalArgumentException(name, "java.time.Instant"); |
838 throw newIllegalArgumentException(name, "java.time.Instant"); |
840 } |
839 } |
841 |
840 |
842 private Instant getInstant(long timestamp, String name) { |
841 private Instant getInstant(long timestamp, String name) { |
843 ValueDescriptor v = getValueDescriptor(descriptors, name, null); |
842 ValueDescriptor v = getValueDescriptor(objectContext.fields, name, null); |
844 Timestamp ts = v.getAnnotation(Timestamp.class); |
843 Timestamp ts = v.getAnnotation(Timestamp.class); |
845 if (ts != null) { |
844 if (ts != null) { |
846 if (timestamp == Long.MIN_VALUE) { |
845 if (timestamp == Long.MIN_VALUE) { |
847 return Instant.MIN; |
846 return Instant.MIN; |
848 } |
847 } |
849 switch (ts.value()) { |
848 switch (ts.value()) { |
850 case Timestamp.MILLISECONDS_SINCE_EPOCH: |
849 case Timestamp.MILLISECONDS_SINCE_EPOCH: |
851 return Instant.ofEpochMilli(timestamp); |
850 return Instant.ofEpochMilli(timestamp); |
852 case Timestamp.TICKS: |
851 case Timestamp.TICKS: |
853 return Instant.ofEpochSecond(0, timeConverter.convertTimestamp(timestamp)); |
852 return Instant.ofEpochSecond(0, objectContext.timeConverter.convertTimestamp(timestamp)); |
854 } |
853 } |
855 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value()); |
854 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with illegal timestamp unit " + ts.value()); |
856 } |
855 } |
857 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timestamp"); |
856 throw new IllegalArgumentException("Attempt to get " + v.getTypeName() + " field \"" + name + "\" with missing @Timestamp"); |
858 } |
857 } |
928 OffsetDateTime getOffsetDateTime(String name) { |
927 OffsetDateTime getOffsetDateTime(String name) { |
929 Instant instant = getInstant(name); |
928 Instant instant = getInstant(name); |
930 if (instant.equals(Instant.MIN)) { |
929 if (instant.equals(Instant.MIN)) { |
931 return OffsetDateTime.MIN; |
930 return OffsetDateTime.MIN; |
932 } |
931 } |
933 return OffsetDateTime.ofInstant(getInstant(name), timeConverter.getZoneOffset()); |
932 return OffsetDateTime.ofInstant(getInstant(name), objectContext.timeConverter.getZoneOffset()); |
934 } |
933 } |
935 |
934 |
936 private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) { |
935 private static IllegalArgumentException newIllegalArgumentException(String name, String typeName) { |
937 return new IllegalArgumentException("Attempt to get field \"" + name + "\" with illegal data type conversion " + typeName); |
936 return new IllegalArgumentException("Attempt to get field \"" + name + "\" with illegal data type conversion " + typeName); |
938 } |
937 } |