Skip to content

Commit f16aba0

Browse files
committed
Add an annotation to define the structure's field order with
1 parent 8ae9187 commit f16aba0

File tree

5 files changed

+279
-82
lines changed

5 files changed

+279
-82
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Features
1616
* Bind `com.sun.jna.platform.win32.Kernel32.ExpandEnvironmentStrings` and add helper method for it as `Kernel32Util#expandEnvironmentStrings` - [@matthiasblaesing](https://github.com/matthiasblaesing).
1717
* [#935](https://github.com/java-native-access/jna/pull/935): Add RegConnectRegistry to Advapi32 mappings. - [@cxcorp](https://github.com/cxcorp).
1818
* [#947](https://github.com/java-native-access/jna/pull/947): Allow retrieval of `ACEs` from `com.sun.jna.platform.win32.WinNT.ACL` even if the contained `ACE` is not currently supported - [@jrobhoward](https://github.com/jrobhoward).
19+
* Add `c.s.j.Structure.FieldOrder` annotation to define the field order of a structures without implementing `Structure#getFieldOrder()` - [@idosu](https://github.com/idosu).
1920

2021
Bug Fixes
2122
---------

src/com/sun/jna/Structure.java

+80-18
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
*/
2424
package com.sun.jna;
2525

26+
import java.lang.annotation.Documented;
27+
import java.lang.annotation.ElementType;
28+
import java.lang.annotation.Retention;
29+
import java.lang.annotation.RetentionPolicy;
30+
import java.lang.annotation.Target;
2631
import java.lang.reflect.Array;
2732
import java.lang.reflect.Constructor;
2833
import java.lang.reflect.Field;
@@ -38,6 +43,7 @@
3843
import java.util.HashSet;
3944
import java.util.Iterator;
4045
import java.util.LinkedHashMap;
46+
import java.util.LinkedList;
4147
import java.util.List;
4248
import java.util.Map;
4349
import java.util.Set;
@@ -66,11 +72,12 @@
6672
* public. If your structure is to have no fields of its own, it must be
6773
* declared abstract.
6874
* </p>
69-
* <p>You <em>must</em> define {@link #getFieldOrder} to return a List of
70-
* field names (Strings) indicating the proper order of the fields. When
71-
* dealing with multiple levels of subclasses of Structure, you must add to
72-
* the list provided by the superclass {@link #getFieldOrder}
73-
* the fields defined in the current class.
75+
* <p>You <em>must</em> annotate the class with {@link FieldOrder} or implement
76+
* {@link #getFieldOrder}, whichever you choose it must contain the field names
77+
* (Strings) indicating the proper order of the fields. If you chose to implement
78+
* {@link #getFieldOrder} notice that when dealing with multiple levels of
79+
* subclasses of Structure, you must add to the list provided by the superclass
80+
* {@link #getFieldOrder} the fields defined in the current class.
7481
* </p>
7582
* <p>In the past, most VMs would return them in a predictable order, but the JVM
7683
* spec does not require it, so {@link #getFieldOrder} is now required to
@@ -865,20 +872,66 @@ protected void writeField(StructField structField) {
865872
}
866873
}
867874

868-
/** Return this Structure's field names in their proper order. For
869-
* example,
875+
/** Used to declare fields order as metadata instead of method.
876+
* example:
870877
* <pre><code>
871-
* protected List getFieldOrder() {
872-
* return Arrays.asList(new String[] { ... });
878+
* // New
879+
* {@literal @}FieldOrder({ "n", "s" })
880+
* class Parent extends Structure {
881+
* public int n;
882+
* public String s;
883+
* }
884+
* {@literal @}FieldOrder({ "d", "c" })
885+
* class Son extends Parent {
886+
* public double d;
887+
* public char c;
888+
* }
889+
* // Old
890+
* class Parent extends Structure {
891+
* public int n;
892+
* public String s;
893+
* protected List<String> getFieldOrder() {
894+
* return Arrays.asList("n", "s");
895+
* }
896+
* }
897+
* class Son extends Parent {
898+
* public double d;
899+
* public char c;
900+
* protected List<String> getFieldOrder() {
901+
* List<String> fields = new LinkedList<String>(super.getFieldOrder());
902+
* fields.addAll(Arrays.asList("d", "c"));
903+
* return fields;
904+
* }
905+
* }
906+
* </code></pre>
907+
*/
908+
@Documented
909+
@Retention(RetentionPolicy.RUNTIME)
910+
@Target(ElementType.TYPE)
911+
public @interface FieldOrder {
912+
String[] value();
913+
}
914+
915+
/** Returns this Structure's field names in their proper order.<br>
916+
*
917+
* When defining a new {@link Structure} you shouldn't override this
918+
* method, but use {@link FieldOrder} annotation to define your field
919+
* order(this also works with inheritance)<br>
920+
*
921+
* If you want to do something non-standard you can override the method
922+
* and define it as followed
923+
* <pre><code>
924+
* protected List<String> getFieldOrder() {
925+
* return Arrays.asList(...);
873926
* }
874927
* </code></pre>
875928
* <strong>IMPORTANT</strong>
876929
* When deriving from an existing Structure subclass, ensure that
877930
* you augment the list provided by the superclass, e.g.
878931
* <pre><code>
879-
* protected List getFieldOrder() {
880-
* List fields = new ArrayList(super.getFieldOrder());
881-
* fields.addAll(Arrays.asList(new String[] { ... }));
932+
* protected List<String> getFieldOrder() {
933+
* List<String> fields = new LinkedList<String>(super.getFieldOrder());
934+
* fields.addAll(Arrays.asList(...));
882935
* return fields;
883936
* }
884937
* </code></pre>
@@ -888,7 +941,19 @@ protected void writeField(StructField structField) {
888941
* guaranteed to be predictable.
889942
* @return ordered list of field names
890943
*/
891-
protected abstract List<String> getFieldOrder();
944+
// TODO(idosu 28 Apr 2018): Maybe deprecate this method to let users know they should use @FieldOrder
945+
protected List<String> getFieldOrder() {
946+
List<String> fields = new LinkedList<String>();
947+
for (Class<?> clazz = getClass(); clazz != Structure.class; clazz = clazz.getSuperclass()) {
948+
FieldOrder order = clazz.getAnnotation(FieldOrder.class);
949+
if (order != null) {
950+
fields.addAll(0, Arrays.asList(order.value()));
951+
}
952+
}
953+
954+
// fields.isEmpty() can be true because it is check somewhere else
955+
return Collections.unmodifiableList(fields);
956+
}
892957

893958
/** Sort the structure fields according to the given array of names.
894959
* @param fields list of fields to be sorted
@@ -1855,6 +1920,7 @@ public String toString() {
18551920
* structure for use by libffi. The lifecycle of this structure is easier
18561921
* to manage on the Java side than in native code.
18571922
*/
1923+
@FieldOrder({ "size", "alignment", "type", "elements" })
18581924
static class FFIType extends Structure {
18591925
public static class size_t extends IntegerType {
18601926
private static final long serialVersionUID = 1L;
@@ -1951,11 +2017,7 @@ private FFIType(Object array, Class<?> type) {
19512017
}
19522018
init(els);
19532019
}
1954-
1955-
@Override
1956-
protected List<String> getFieldOrder() {
1957-
return Arrays.asList(new String[] { "size", "alignment", "type", "elements" });
1958-
}
2020+
19592021
private void init(Pointer[] els) {
19602022
elements = new Memory(Native.POINTER_SIZE * els.length);
19612023
elements.write(0, els, 0, els.length);

0 commit comments

Comments
 (0)