1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
/*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Utility class for detecting missing {@link com.sun.jna.Structure#getFieldOrder()} methods.
*
* This class could be moved to the unit test tree, but then reusing it in the 'platform' project would require
* publishing this test tree.
*
* @author Dan Rollo
* Date: 1/17/13
* Time: 4:08 PM
*/
public final class StructureFieldOrderInspector {
private StructureFieldOrderInspector(){}
/**
* Search for Structure sub types in the source tree of the given class, and validate the getFieldOrder() method,
* and collects all errors into one exception.
*
* @param classDeclaredInSourceTreeToSearch a class who's source tree will be searched for Structure sub types.
* @param ignoreConstructorError list of classname prefixes for which to ignore construction errors.
*/
public static void batchCheckStructureGetFieldOrder(final Class<?> classDeclaredInSourceTreeToSearch,
final List<String> ignoreConstructorError) {
batchCheckStructureGetFieldOrder(classDeclaredInSourceTreeToSearch, ignoreConstructorError, false);
}
/**
* Search for Structure sub types in the source tree of the given class, and validate the getFieldOrder() method,
* and collects all errors into one exception.
*
* @param classDeclaredInSourceTreeToSearch a class who's source tree will be searched for Structure sub types.
* @param ignoreConstructorError list of classname prefixes for which to ignore construction errors.
* @param onlyInnerClasses limit scan to inner classes of the supplied class
*/
public static void batchCheckStructureGetFieldOrder(final Class<?> classDeclaredInSourceTreeToSearch,
final List<String> ignoreConstructorError,
final boolean onlyInnerClasses) {
final Set<Class<? extends Structure>> classes = StructureFieldOrderInspector.findSubTypesOfStructure(classDeclaredInSourceTreeToSearch, onlyInnerClasses);
final List<Throwable> problems = new ArrayList<>();
for (final Class<? extends Structure> structureSubType : classes) {
try {
StructureFieldOrderInspector.checkMethodGetFieldOrder(structureSubType, ignoreConstructorError);
} catch (Throwable t) {
problems.add(t);
}
}
if (problems.size() > 0) {
String msg = "";
for (final Throwable t : problems) {
msg += t.getMessage() + "; \n";
}
throw new RuntimeException("Some Structure sub types (" + problems.size() + ") have problems with getFieldOrder(): \n" + msg);
}
}
/**
* Search for Structure sub types in the source tree of the given class, and validate the getFieldOrder() method.
*
* @param classDeclaredInSourceTreeToSearch a class who's source tree will be searched for Structure sub types.
* @param ignoreConstructorError list of classname prefixes for which to ignore construction errors.
*/
public static void checkStructureGetFieldOrder(final Class<?> classDeclaredInSourceTreeToSearch,
final List<String> ignoreConstructorError) {
final Set<Class<? extends Structure>> classes = StructureFieldOrderInspector.findSubTypesOfStructure(classDeclaredInSourceTreeToSearch);
for (final Class<? extends Structure> structureSubType : classes) {
StructureFieldOrderInspector.checkMethodGetFieldOrder(structureSubType, ignoreConstructorError);
}
}
/**
* Find all classes that extend {@link Structure}.
*/
public static Set<Class<? extends Structure>> findSubTypesOfStructure(final Class<?> classDeclaredInSourceTreeToSearch, boolean onlyInnerClasses) {
// use: http://code.google.com/p/reflections/
final Reflections reflections = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.setUrls(ClasspathHelper.forClass(classDeclaredInSourceTreeToSearch))
);
Set<Class<? extends Structure>> types = new HashSet<>(reflections.getSubTypesOf(Structure.class));
if(onlyInnerClasses) {
Iterator<Class<? extends Structure>> it = types.iterator();
while(it.hasNext()) {
if(! (it.next().getEnclosingClass() == classDeclaredInSourceTreeToSearch)) {
it.remove();
}
}
}
return types;
}
/**
* Find all classes that extend {@link Structure}.
*/
public static Set<Class<? extends Structure>> findSubTypesOfStructure(final Class<?> classDeclaredInSourceTreeToSearch) {
return findSubTypesOfStructure(classDeclaredInSourceTreeToSearch, false);
}
public static void checkMethodGetFieldOrder(final Class<? extends Structure> structureSubType,
final List<String> ignoreConstructorError) {
if (Structure.ByValue.class.isAssignableFrom(structureSubType)
|| Structure.ByReference.class.isAssignableFrom(structureSubType)) {
// ignore tagging interfaces
return;
}
if (Modifier.isAbstract(structureSubType.getModifiers())) {
// do not try to construct abstract Structure sub types
return;
}
final Constructor<? extends Structure> structConstructor;
try {
structConstructor = structureSubType.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
if (structureSubType == Structure.FFIType.class) {
// ignore this case
// @todo Allow user to pass in list of classes for which to skip construction?
return;
}
throw new RuntimeException("Parameterless constructor failed on Structure sub type: " + structureSubType.getName());
}
if (!structConstructor.isAccessible()) {
structConstructor.setAccessible(true);
}
final Structure structure;
try {
structure= structConstructor.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Could not instantiate Structure sub type: " + structureSubType.getName(), e);
} catch (InvocationTargetException e) {
// this is triggered by checks in Structure.getFields(), and static loadlibrary() failures
if (ignoreConstructorError != null) {
final String structSubtypeName = structureSubType.getName();
for (final String classPrefix : ignoreConstructorError) {
if (structSubtypeName.startsWith(classPrefix)) {
return;
}
}
}
throw new RuntimeException("Could not instantiate Structure sub type: " + structureSubType.getName(), e);
}
final List<String> methodCallFieldOrder = structure.getFieldOrder();
final List<Field> actualFields = structure.getFieldList();
final List<String> actualFieldNames = new ArrayList<>(actualFields.size());
for (final Field field : actualFields) {
// ignore static fields
if (!Modifier.isStatic(field.getModifiers())) {
final String actualFieldName = field.getName();
if (!methodCallFieldOrder.contains(actualFieldName)) {
throw new IllegalArgumentException(structureSubType.getName() + ".getFieldOrder() [" + methodCallFieldOrder
+ "] does not include declared field: " + actualFieldName);
}
actualFieldNames.add(actualFieldName);
}
}
for (final String methodCallField : methodCallFieldOrder) {
if (!actualFieldNames.contains(methodCallField)) {
throw new IllegalArgumentException(structureSubType.getName() + ".getFieldOrder() [" + methodCallFieldOrder
+ "] includes undeclared field: " + methodCallField);
}
}
}
}
|