package com.hwlcn.ldap.ldap.sdk.persist; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.List; import com.hwlcn.ldap.ldap.sdk.Attribute; import com.hwlcn.ldap.ldap.sdk.Entry; import com.hwlcn.ldap.ldap.sdk.schema.AttributeTypeDefinition; import com.hwlcn.core.annotation.NotMutable; import com.hwlcn.core.annotation.ThreadSafety; import com.hwlcn.ldap.util.ThreadSafetyLevel; import static com.hwlcn.ldap.ldap.sdk.persist.PersistMessages.*; import static com.hwlcn.ldap.util.Debug.*; import static com.hwlcn.ldap.util.StaticUtils.*; import static com.hwlcn.ldap.util.Validator.*; @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public final class FieldInfo implements Serializable { private static final long serialVersionUID = -5715642176677596417L; private final boolean failOnInvalidValue; private final boolean failOnTooManyValues; private final boolean includeInAdd; private final boolean includeInModify; private final boolean includeInRDN; private final boolean isRequiredForDecode; private final boolean isRequiredForEncode; private final boolean lazilyLoad; private final boolean supportsMultipleValues; private final Class<?> containingClass; private final Field field; private final FilterUsage filterUsage; private final ObjectEncoder encoder; private final String attributeName; private final String[] defaultDecodeValues; private final String[] defaultEncodeValues; private final String[] objectClasses; FieldInfo(final Field f, final Class<?> c) throws LDAPPersistException { ensureNotNull(f, c); field = f; f.setAccessible(true); final LDAPField a = f.getAnnotation(LDAPField.class); if (a == null) { throw new LDAPPersistException(ERR_FIELD_INFO_FIELD_NOT_ANNOTATED.get( f.getName(), c.getName())); } final LDAPObject o = c.getAnnotation(LDAPObject.class); if (o == null) { throw new LDAPPersistException(ERR_FIELD_INFO_CLASS_NOT_ANNOTATED.get( c.getName())); } containingClass = c; failOnInvalidValue = a.failOnInvalidValue(); includeInRDN = a.inRDN(); includeInAdd = (includeInRDN || a.inAdd()); includeInModify = ((! includeInRDN) && a.inModify()); filterUsage = a.filterUsage(); lazilyLoad = a.lazilyLoad(); isRequiredForDecode = (a.requiredForDecode() && (! lazilyLoad)); isRequiredForEncode = (includeInRDN || a.requiredForEncode()); defaultDecodeValues = a.defaultDecodeValue(); defaultEncodeValues = a.defaultEncodeValue(); if (lazilyLoad) { if (defaultDecodeValues.length > 0) { throw new LDAPPersistException( ERR_FIELD_INFO_LAZY_WITH_DEFAULT_DECODE.get(f.getName(), c.getName())); } if (defaultEncodeValues.length > 0) { throw new LDAPPersistException( ERR_FIELD_INFO_LAZY_WITH_DEFAULT_ENCODE.get(f.getName(), c.getName())); } if (includeInRDN) { throw new LDAPPersistException(ERR_FIELD_INFO_LAZY_IN_RDN.get( f.getName(), c.getName())); } } final int modifiers = f.getModifiers(); if (Modifier.isFinal(modifiers)) { throw new LDAPPersistException(ERR_FIELD_INFO_FIELD_FINAL.get( f.getName(), c.getName())); } if (Modifier.isStatic(modifiers)) { throw new LDAPPersistException(ERR_FIELD_INFO_FIELD_STATIC.get( f.getName(), c.getName())); } try { encoder = a.encoderClass().newInstance(); } catch (Exception e) { debugException(e); throw new LDAPPersistException(ERR_FIELD_INFO_CANNOT_GET_ENCODER.get( a.encoderClass().getName(), f.getName(), c.getName(), getExceptionMessage(e)), e); } if (! encoder.supportsType(f.getGenericType())) { throw new LDAPPersistException( ERR_FIELD_INFO_ENCODER_UNSUPPORTED_TYPE.get( encoder.getClass().getName(), f.getName(), c.getName(), f.getGenericType())); } supportsMultipleValues = encoder.supportsMultipleValues(f); if (supportsMultipleValues) { failOnTooManyValues = false; } else { failOnTooManyValues = a.failOnTooManyValues(); if (defaultDecodeValues.length > 1) { throw new LDAPPersistException( ERR_FIELD_INFO_UNSUPPORTED_MULTIPLE_DEFAULT_DECODE_VALUES.get( f.getName(), c.getName())); } if (defaultEncodeValues.length > 1) { throw new LDAPPersistException( ERR_FIELD_INFO_UNSUPPORTED_MULTIPLE_DEFAULT_ENCODE_VALUES.get( f.getName(), c.getName())); } } final String attrName = a.attribute(); if ((attrName == null) || (attrName.length() == 0)) { attributeName = f.getName(); } else { attributeName = attrName; } final StringBuilder invalidReason = new StringBuilder(); if (! PersistUtils.isValidLDAPName(attributeName, true, invalidReason)) { throw new LDAPPersistException(ERR_FIELD_INFO_INVALID_ATTR_NAME.get( f.getName(), c.getName(), invalidReason.toString())); } final String structuralClass; if (o.structuralClass().length() == 0) { structuralClass = getUnqualifiedClassName(c); } else { structuralClass = o.structuralClass(); } final String[] ocs = a.objectClass(); if ((ocs == null) || (ocs.length == 0)) { objectClasses = new String[] { structuralClass }; } else { objectClasses = ocs; } for (final String s : objectClasses) { if (! s.equalsIgnoreCase(structuralClass)) { boolean found = false; for (final String oc : o.auxiliaryClass()) { if (s.equalsIgnoreCase(oc)) { found = true; break; } } if (! found) { throw new LDAPPersistException(ERR_FIELD_INFO_INVALID_OC.get( f.getName(), c.getName(), s)); } } } } public Field getField() { return field; } public Class<?> getContainingClass() { return containingClass; } public boolean failOnInvalidValue() { return failOnInvalidValue; } public boolean failOnTooManyValues() { return failOnTooManyValues; } public boolean includeInAdd() { return includeInAdd; } public boolean includeInModify() { return includeInModify; } public boolean includeInRDN() { return includeInRDN; } public FilterUsage getFilterUsage() { return filterUsage; } public boolean isRequiredForDecode() { return isRequiredForDecode; } public boolean isRequiredForEncode() { return isRequiredForEncode; } public boolean lazilyLoad() { return lazilyLoad; } public ObjectEncoder getEncoder() { return encoder; } public String getAttributeName() { return attributeName; } public String[] getDefaultDecodeValues() { return defaultDecodeValues; } public String[] getDefaultEncodeValues() { return defaultEncodeValues; } public String[] getObjectClasses() { return objectClasses; } public boolean supportsMultipleValues() { return supportsMultipleValues; } AttributeTypeDefinition constructAttributeType() throws LDAPPersistException { return constructAttributeType(DefaultOIDAllocator.getInstance()); } AttributeTypeDefinition constructAttributeType(final OIDAllocator a) throws LDAPPersistException { return encoder.constructAttributeType(field, a); } Attribute encode(final Object o, final boolean ignoreRequiredFlag) throws LDAPPersistException { try { final Object fieldValue = field.get(o); if (fieldValue == null) { if (defaultEncodeValues.length > 0) { return new Attribute(attributeName, defaultEncodeValues); } if (isRequiredForEncode && (! ignoreRequiredFlag)) { throw new LDAPPersistException( ERR_FIELD_INFO_MISSING_REQUIRED_VALUE.get(field.getName(), containingClass.getName())); } return null; } return encoder.encodeFieldValue(field, fieldValue, attributeName); } catch (LDAPPersistException lpe) { debugException(lpe); throw lpe; } catch (Exception e) { debugException(e); throw new LDAPPersistException(ERR_FIELD_INFO_CANNOT_ENCODE.get( field.getName(), containingClass.getName(), getExceptionMessage(e)), e); } } boolean decode(final Object o, final Entry e, final List<String> failureReasons) { boolean successful = true; Attribute a = e.getAttribute(attributeName); if ((a == null) || (! a.hasValue())) { if (defaultDecodeValues.length > 0) { a = new Attribute(attributeName, defaultDecodeValues); } else { if (isRequiredForDecode) { successful = false; failureReasons.add(ERR_FIELD_INFO_MISSING_REQUIRED_ATTRIBUTE.get( containingClass.getName(), e.getDN(), attributeName, field.getName())); } try { encoder.setNull(field, o); } catch (final LDAPPersistException lpe) { debugException(lpe); successful = false; failureReasons.add(lpe.getMessage()); } return successful; } } if (failOnTooManyValues && (a.size() > 1)) { successful = false; failureReasons.add(ERR_FIELD_INFO_FIELD_NOT_MULTIVALUED.get(a.getName(), field.getName(), containingClass.getName())); } try { encoder.decodeField(field, o, a); } catch (LDAPPersistException lpe) { debugException(lpe); if (failOnInvalidValue) { successful = false; failureReasons.add(lpe.getMessage()); } } return successful; } }