/* Copyright (c) 2007-2012 Timothy Wall, All Rights Reserved * * 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 java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; /** Represents a native union. When writing to native memory, the field * corresponding to the type passed to {@link #setType} will be written * to native memory. Upon reading from native memory, Structure, String, * or WString fields will <em>not</em> be initialized unless they are * the current field as identified by a call to {@link #setType}. The current * field is always unset by default to avoid accidentally attempting to read * a field that is not valid. In the case of a String, for instance, an * invalid pointer may result in a memory fault when attempting to initialize * the String. */ public abstract class Union extends Structure { private StructField activeField; /** Create a Union whose size and alignment will be calculated * automatically. */ protected Union() { } /** Create a Union of the given size, using default alignment. */ protected Union(Pointer p) { super(p); } /** Create a Union of the given size and alignment type. */ protected Union(Pointer p, int alignType) { super(p, alignType); } /** Create a Union of the given size and alignment type. */ protected Union(TypeMapper mapper) { super(mapper); } /** Create a Union of the given size and alignment type. */ protected Union(Pointer p, int alignType, TypeMapper mapper) { super(p, alignType, mapper); } /** Unions do not need a field order, so automatically provide a value to * satisfy checking in the Structure superclass. */ @Override protected List<String> getFieldOrder() { List<Field> flist = getFieldList(); List<String> list = new ArrayList<String>(flist.size()); for (Field f : flist) { list.add(f.getName()); } return list; } /** Indicates by type which field will be used to write to native memory. * If there are multiple fields of the same type, use {@link * #setType(String)} instead with the field name. * @param type desired active type for the union * @throws IllegalArgumentException if the type does not correspond to * any declared union field. */ public void setType(Class<?> type) { ensureAllocated(); for (StructField f : fields().values()) { if (f.type == type) { activeField = f; return; } } throw new IllegalArgumentException("No field of type " + type + " in " + this); } /** * Indicates which field will be used to write to native memory. * @param fieldName desired field to use for the active union type * @throws IllegalArgumentException if the name does not correspond to * any declared union field. */ public void setType(String fieldName) { ensureAllocated(); StructField f = fields().get(fieldName); if (f != null) { activeField = f; } else { throw new IllegalArgumentException("No field named " + fieldName + " in " + this); } } /** Force a read of the given field from native memory. * @return the new field value, after updating * @throws IllegalArgumentException if no field exists with the given name */ @Override public Object readField(String fieldName) { ensureAllocated(); setType(fieldName); return super.readField(fieldName); } /** Write the given field value to native memory. * The given field will become the active one. * @throws IllegalArgumentException if no field exists with the given name */ @Override public void writeField(String fieldName) { ensureAllocated(); setType(fieldName); super.writeField(fieldName); } /** Write the given field value to the field and native memory. * The given field will become the active one. * @throws IllegalArgumentException if no field exists with the given name */ @Override public void writeField(String fieldName, Object value) { ensureAllocated(); setType(fieldName); super.writeField(fieldName, value); } /** Reads the Structure field of the given type from memory, sets it as * the active type and returns it. Convenience method for * <pre><code> * Union u; * Class type; * u.setType(type); * u.read(); * value = u.<i>field</i>; * </code></pre> * @param type class type of the Structure field to read * @return the Structure field with the given type */ public Object getTypedValue(Class<?> type) { ensureAllocated(); for (StructField f : fields().values()) { if (f.type == type) { activeField = f; read(); return getFieldValue(activeField.field); } } throw new IllegalArgumentException("No field of type " + type + " in " + this); } /** Set the active type and its value. Convenience method for * <pre><code> * Union u; * Class type; * u.setType(type); * u.<i>field</i> = value; * </code></pre> * @param object instance of a class which is part of the union * @return this Union object */ public Object setTypedValue(Object object) { StructField f = findField(object.getClass()); if (f != null) { activeField = f; setFieldValue(f.field, object); return this; } throw new IllegalArgumentException("No field of type " + object.getClass() + " in " + this); } /** Returns the field in this union with the same type as <code>type</code>, * if any, null otherwise. * @param type type to search for * @return StructField of matching type */ private StructField findField(Class<?> type) { ensureAllocated(); for (StructField f : fields().values()) { if (f.type.isAssignableFrom(type)) { return f; } } return null; } /** Only the currently selected field will be written. */ @Override protected void writeField(StructField field) { if (field == activeField) { super.writeField(field); } } /** Avoid reading pointer-based fields and structures unless explicitly * selected. Structures may contain pointer-based fields which can * crash the VM if not properly initialized. */ @Override protected Object readField(StructField field) { if (field == activeField || (!Structure.class.isAssignableFrom(field.type) && !String.class.isAssignableFrom(field.type) && !WString.class.isAssignableFrom(field.type))) { return super.readField(field); } // Field not accessible // TODO: read by-value structures, to the extent possible; need a // "read cautiously" method to "read" to indicate we want to avoid // pointer-based fields return null; } /** All fields are considered the "first" element. */ @Override protected int getNativeAlignment(Class<?> type, Object value, boolean isFirstElement) { return super.getNativeAlignment(type, value, true); } }