Skip to content

Commit

Permalink
Add an annotation to define the field order of a structure
Browse files Browse the repository at this point in the history
  • Loading branch information
idosu authored and matthiasblaesing committed May 10, 2018
1 parent 5936a34 commit a59da1e
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 82 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Features
* Bind `com.sun.jna.platform.win32.Kernel32.ExpandEnvironmentStrings` and add helper method for it as `Kernel32Util#expandEnvironmentStrings` - [@matthiasblaesing](https://github.com/matthiasblaesing).
* [#935](https://github.com/java-native-access/jna/pull/935): Add RegConnectRegistry to Advapi32 mappings. - [@cxcorp](https://github.com/cxcorp).
* [#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).
* Add `c.s.j.Structure.FieldOrder` annotation to define the field order of a structures without implementing `Structure#getFieldOrder()` - [@idosu](https://github.com/idosu).

Bug Fixes
---------
Expand Down
98 changes: 80 additions & 18 deletions src/com/sun/jna/Structure.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
*/
package com.sun.jna;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
Expand All @@ -38,6 +43,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -66,11 +72,12 @@
* public. If your structure is to have no fields of its own, it must be
* declared abstract.
* </p>
* <p>You <em>must</em> define {@link #getFieldOrder} to return a List of
* field names (Strings) indicating the proper order of the fields. When
* dealing with multiple levels of subclasses of Structure, you must add to
* the list provided by the superclass {@link #getFieldOrder}
* the fields defined in the current class.
* <p>You <em>must</em> annotate the class with {@link FieldOrder} or implement
* {@link #getFieldOrder}, whichever you choose it must contain the field names
* (Strings) indicating the proper order of the fields. If you chose to implement
* {@link #getFieldOrder} notice that when dealing with multiple levels of
* subclasses of Structure, you must add to the list provided by the superclass
* {@link #getFieldOrder} the fields defined in the current class.
* </p>
* <p>In the past, most VMs would return them in a predictable order, but the JVM
* spec does not require it, so {@link #getFieldOrder} is now required to
Expand Down Expand Up @@ -865,20 +872,66 @@ protected void writeField(StructField structField) {
}
}

/** Return this Structure's field names in their proper order. For
* example,
/** Used to declare fields order as metadata instead of method.
* example:
* <pre><code>
* protected List getFieldOrder() {
* return Arrays.asList(new String[] { ... });
* // New
* {@literal @}FieldOrder({ "n", "s" })
* class Parent extends Structure {
* public int n;
* public String s;
* }
* {@literal @}FieldOrder({ "d", "c" })
* class Son extends Parent {
* public double d;
* public char c;
* }
* // Old
* class Parent extends Structure {
* public int n;
* public String s;
* protected List<String> getFieldOrder() {
* return Arrays.asList("n", "s");
* }
* }
* class Son extends Parent {
* public double d;
* public char c;
* protected List<String> getFieldOrder() {
* List<String> fields = new LinkedList<String>(super.getFieldOrder());
* fields.addAll(Arrays.asList("d", "c"));
* return fields;
* }
* }
* </code></pre>
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FieldOrder {
String[] value();
}

/** Returns this Structure's field names in their proper order.<br>
*
* When defining a new {@link Structure} you shouldn't override this
* method, but use {@link FieldOrder} annotation to define your field
* order(this also works with inheritance)<br>
*
* If you want to do something non-standard you can override the method
* and define it as followed
* <pre><code>
* protected List<String> getFieldOrder() {
* return Arrays.asList(...);
* }
* </code></pre>
* <strong>IMPORTANT</strong>
* When deriving from an existing Structure subclass, ensure that
* you augment the list provided by the superclass, e.g.
* <pre><code>
* protected List getFieldOrder() {
* List fields = new ArrayList(super.getFieldOrder());
* fields.addAll(Arrays.asList(new String[] { ... }));
* protected List<String> getFieldOrder() {
* List<String> fields = new LinkedList<String>(super.getFieldOrder());
* fields.addAll(Arrays.asList(...));
* return fields;
* }
* </code></pre>
Expand All @@ -888,7 +941,19 @@ protected void writeField(StructField structField) {
* guaranteed to be predictable.
* @return ordered list of field names
*/
protected abstract List<String> getFieldOrder();
// TODO(idosu 28 Apr 2018): Maybe deprecate this method to let users know they should use @FieldOrder
protected List<String> getFieldOrder() {
List<String> fields = new LinkedList<String>();
for (Class<?> clazz = getClass(); clazz != Structure.class; clazz = clazz.getSuperclass()) {
FieldOrder order = clazz.getAnnotation(FieldOrder.class);
if (order != null) {
fields.addAll(0, Arrays.asList(order.value()));
}
}

// fields.isEmpty() can be true because it is check somewhere else
return Collections.unmodifiableList(fields);
}

/** Sort the structure fields according to the given array of names.
* @param fields list of fields to be sorted
Expand Down Expand Up @@ -1855,6 +1920,7 @@ public String toString() {
* structure for use by libffi. The lifecycle of this structure is easier
* to manage on the Java side than in native code.
*/
@FieldOrder({ "size", "alignment", "type", "elements" })
static class FFIType extends Structure {
public static class size_t extends IntegerType {
private static final long serialVersionUID = 1L;
Expand Down Expand Up @@ -1951,11 +2017,7 @@ private FFIType(Object array, Class<?> type) {
}
init(els);
}

@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "size", "alignment", "type", "elements" });
}

private void init(Pointer[] els) {
elements = new Memory(Native.POINTER_SIZE * els.length);
elements.write(0, els, 0, els.length);
Expand Down
Loading

0 comments on commit a59da1e

Please sign in to comment.