001/* 002 * Copyright 2009-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2009-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.persist; 037 038 039 040import java.io.Serializable; 041import java.lang.reflect.Field; 042import java.lang.reflect.Method; 043import java.lang.reflect.Type; 044 045import com.unboundid.ldap.sdk.Attribute; 046import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 047import com.unboundid.util.Debug; 048import com.unboundid.util.Extensible; 049import com.unboundid.util.NotNull; 050import com.unboundid.util.StaticUtils; 051import com.unboundid.util.ThreadSafety; 052import com.unboundid.util.ThreadSafetyLevel; 053 054import static com.unboundid.ldap.sdk.persist.PersistMessages.*; 055 056 057 058/** 059 * This class provides an API for converting between Java objects and LDAP 060 * attributes. Concrete instances of this class must provide a default 061 * zero-argument constructor. 062 */ 063@Extensible() 064@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 065public abstract class ObjectEncoder 066 implements Serializable 067{ 068 /** 069 * The serial version UID for this serializable class. 070 */ 071 private static final long serialVersionUID = -5139516629886911696L; 072 073 074 075 /** 076 * Indicates whether this object encoder may be used to encode or decode 077 * objects of the specified type. 078 * 079 * @param t The type of object for which to make the determination. 080 * 081 * @return {@code true} if this object encoder may be used for objects of 082 * the specified type, or {@code false} if not. 083 */ 084 public abstract boolean supportsType(@NotNull Type t); 085 086 087 088 /** 089 * Constructs a definition for an LDAP attribute type which may be added to 090 * the directory server schema to allow it to hold the value of the specified 091 * field. Note that the object identifier used for the constructed attribute 092 * type definition is not required to be valid or unique. 093 * 094 * @param f The field for which to construct an LDAP attribute type 095 * definition. It will include the {@link LDAPField} annotation 096 * type. 097 * 098 * @return The constructed attribute type definition. 099 * 100 * @throws LDAPPersistException If this object encoder does not support 101 * encoding values for the associated field 102 * type. 103 */ 104 @NotNull() 105 public final AttributeTypeDefinition constructAttributeType( 106 @NotNull final Field f) 107 throws LDAPPersistException 108 { 109 return constructAttributeType(f, DefaultOIDAllocator.getInstance()); 110 } 111 112 113 114 /** 115 * Constructs a definition for an LDAP attribute type which may be added to 116 * the directory server schema to allow it to hold the value of the specified 117 * field. 118 * 119 * @param f The field for which to construct an LDAP attribute type 120 * definition. It will include the {@link LDAPField} annotation 121 * type. 122 * @param a The OID allocator to use to generate the object identifier. It 123 * must not be {@code null}. 124 * 125 * @return The constructed attribute type definition. 126 * 127 * @throws LDAPPersistException If this object encoder does not support 128 * encoding values for the associated field 129 * type. 130 */ 131 @NotNull() 132 public abstract AttributeTypeDefinition constructAttributeType( 133 @NotNull Field f, 134 @NotNull OIDAllocator a) 135 throws LDAPPersistException; 136 137 138 139 /** 140 * Constructs a definition for an LDAP attribute type which may be added to 141 * the directory server schema to allow it to hold the value returned by the 142 * specified method. Note that the object identifier used for the constructed 143 * attribute type definition is not required to be valid or unique. 144 * 145 * @param m The method for which to construct an LDAP attribute type 146 * definition. It will include the {@link LDAPGetter} 147 * annotation type. 148 * 149 * @return The constructed attribute type definition. 150 * 151 * @throws LDAPPersistException If this object encoder does not support 152 * encoding values for the associated method 153 * type. 154 */ 155 @NotNull() 156 public final AttributeTypeDefinition constructAttributeType( 157 @NotNull final Method m) 158 throws LDAPPersistException 159 { 160 return constructAttributeType(m, DefaultOIDAllocator.getInstance()); 161 } 162 163 164 165 /** 166 * Constructs a definition for an LDAP attribute type which may be added to 167 * the directory server schema to allow it to hold the value returned by the 168 * specified method. Note that the object identifier used for the constructed 169 * attribute type definition is not required to be valid or unique. 170 * 171 * @param m The method for which to construct an LDAP attribute type 172 * definition. It will include the {@link LDAPGetter} 173 * annotation type. 174 * @param a The OID allocator to use to generate the object identifier. It 175 * must not be {@code null}. 176 * 177 * @return The constructed attribute type definition. 178 * 179 * @throws LDAPPersistException If this object encoder does not support 180 * encoding values for the associated method 181 * type. 182 */ 183 @NotNull() 184 public abstract AttributeTypeDefinition constructAttributeType( 185 @NotNull Method m, 186 @NotNull OIDAllocator a) 187 throws LDAPPersistException; 188 189 190 191 /** 192 * Indicates whether the provided field can hold multiple values. 193 * 194 * @param field The field for which to make the determination. It must be 195 * marked with the {@link LDAPField} annotation. 196 * 197 * @return {@code true} if the provided field can hold multiple values, or 198 * {@code false} if not. 199 */ 200 public abstract boolean supportsMultipleValues(@NotNull Field field); 201 202 203 204 /** 205 * Indicates whether the provided setter method takes an argument that can 206 * hold multiple values. 207 * 208 * @param method The setter method for which to make the determination. It 209 * must be marked with the {@link LDAPSetter} annotation 210 * type and conform to the constraints associated with that 211 * annotation. 212 * 213 * @return {@code true} if the provided method takes an argument that can 214 * hold multiple values, or {@code false} if not. 215 */ 216 public abstract boolean supportsMultipleValues(@NotNull Method method); 217 218 219 220 /** 221 * Encodes the provided field to an LDAP attribute. 222 * 223 * @param field The field to be encoded. 224 * @param value The value for the field in the object to be encoded. 225 * @param name The name to use for the constructed attribute. 226 * 227 * @return The attribute containing the encoded representation of the 228 * provided field. 229 * 230 * @throws LDAPPersistException If a problem occurs while attempting to 231 * construct an attribute for the field. 232 */ 233 @NotNull() 234 public abstract Attribute encodeFieldValue(@NotNull Field field, 235 @NotNull Object value, 236 @NotNull String name) 237 throws LDAPPersistException; 238 239 240 241 /** 242 * Encodes the provided method to an LDAP attribute. 243 * 244 * @param method The method to be encoded. 245 * @param value The value returned by the method in the object to be 246 * encoded. 247 * @param name The name to use for the constructed attribute. 248 * 249 * @return The attribute containing the encoded representation of the 250 * provided method value. 251 * 252 * @throws LDAPPersistException If a problem occurs while attempting to 253 * construct an attribute for the method. 254 */ 255 @NotNull() 256 public abstract Attribute encodeMethodValue(@NotNull Method method, 257 @NotNull Object value, 258 @NotNull String name) 259 throws LDAPPersistException; 260 261 262 263 /** 264 * Updates the provided object to assign a value for the specified field from 265 * the contents of the given attribute. 266 * 267 * @param field The field to update in the provided object. 268 * @param object The object to be updated. 269 * @param attribute The attribute whose value(s) should be used to update 270 * the specified field in the given object. 271 * 272 * @throws LDAPPersistException If a problem occurs while attempting to 273 * assign a value to the specified field. 274 */ 275 public abstract void decodeField(@NotNull Field field, @NotNull Object object, 276 @NotNull Attribute attribute) 277 throws LDAPPersistException; 278 279 280 281 /** 282 * Assigns a {@code null} value to the provided field, if possible. If the 283 * field type is primitive and cannot be assigned a {@code null} value, then a 284 * default primitive value will be assigned instead (0 for numeric values, 285 * false for {@code boolean} values, and the null character for {@code char} 286 * values). 287 * 288 * @param f The field to which the {@code null} value should be assigned. 289 * It must not be {@code null} and must be marked with the 290 * {@link LDAPField} annotation. 291 * @param o The object to be updated. It must not be {@code null}, and the 292 * class must be marked with the {@link LDAPObject annotation}. 293 * 294 * @throws LDAPPersistException If a problem occurs while attempting to 295 * assign a {@code null} value to the specified 296 * field. 297 */ 298 public void setNull(@NotNull final Field f, @NotNull final Object o) 299 throws LDAPPersistException 300 { 301 try 302 { 303 f.setAccessible(true); 304 305 final Class<?> type = f.getType(); 306 if (type.equals(Boolean.TYPE)) 307 { 308 f.set(o, Boolean.FALSE); 309 } 310 else if (type.equals(Byte.TYPE)) 311 { 312 f.set(o, (byte) 0); 313 } 314 else if (type.equals(Character.TYPE)) 315 { 316 f.set(o, '\u0000'); 317 } 318 else if (type.equals(Double.TYPE)) 319 { 320 f.set(o, 0.0d); 321 } 322 else if (type.equals(Float.TYPE)) 323 { 324 f.set(o, 0.0f); 325 } 326 else if (type.equals(Integer.TYPE)) 327 { 328 f.set(o, 0); 329 } 330 else if (type.equals(Long.TYPE)) 331 { 332 f.set(o, 0L); 333 } 334 else if (type.equals(Short.TYPE)) 335 { 336 f.set(o, (short) 0); 337 } 338 else 339 { 340 f.set(o, null); 341 } 342 } 343 catch (final Exception e) 344 { 345 Debug.debugException(e); 346 throw new LDAPPersistException( 347 ERR_ENCODER_CANNOT_SET_NULL_FIELD_VALUE.get(f.getName(), 348 o.getClass().getName(), StaticUtils.getExceptionMessage(e)), 349 e); 350 } 351 } 352 353 354 355 /** 356 * Invokes the provided setter method with a single argument that will set a 357 * {@code null} value for that method, if possible. If the argument type is 358 * and cannot be assigned a {@code null} value, then a default primitive value 359 * will be assigned instead (0 for numeric values, false for {@code boolean} 360 * values, and the null character for {@code char} values). 361 * 362 * @param m The setter method that should be used to set the {@code null} 363 * value. It must not be {@code null}, and must have the 364 * {@code LDAPSetter} annotation. 365 * @param o The object to be updated. It must not be {@code null}, and the 366 * class must be marked with the {@link LDAPObject annotation}. 367 * 368 * @throws LDAPPersistException If a problem occurs while attempting to 369 * assign a {@code null} value to the specified 370 * field. 371 */ 372 public void setNull(@NotNull final Method m, @NotNull final Object o) 373 throws LDAPPersistException 374 { 375 try 376 { 377 m.setAccessible(true); 378 379 final Class<?> type = m.getParameterTypes()[0]; 380 if (type.equals(Boolean.TYPE)) 381 { 382 m.invoke(o, Boolean.FALSE); 383 } 384 else if (type.equals(Byte.TYPE)) 385 { 386 m.invoke(o, (byte) 0); 387 } 388 else if (type.equals(Character.TYPE)) 389 { 390 m.invoke(o, '\u0000'); 391 } 392 else if (type.equals(Double.TYPE)) 393 { 394 m.invoke(o, 0.0d); 395 } 396 else if (type.equals(Float.TYPE)) 397 { 398 m.invoke(o, 0.0f); 399 } 400 else if (type.equals(Integer.TYPE)) 401 { 402 m.invoke(o, 0); 403 } 404 else if (type.equals(Long.TYPE)) 405 { 406 m.invoke(o, 0L); 407 } 408 else if (type.equals(Short.TYPE)) 409 { 410 m.invoke(o, (short) 0); 411 } 412 else 413 { 414 m.invoke(o, type.cast(null)); 415 } 416 } 417 catch (final Exception e) 418 { 419 Debug.debugException(e); 420 throw new LDAPPersistException( 421 ERR_ENCODER_CANNOT_SET_NULL_METHOD_VALUE.get(m.getName(), 422 o.getClass().getName(), StaticUtils.getExceptionMessage(e)), 423 e); 424 } 425 } 426 427 428 429 /** 430 * Updates the provided object to invoke the specified method to set a value 431 * from the contents of the given attribute. 432 * 433 * @param method The method to invoke in the provided object. 434 * @param object The object to be updated. 435 * @param attribute The attribute whose value(s) should be used to update 436 * the specified method in the given object. 437 * 438 * @throws LDAPPersistException If a problem occurs while attempting to 439 * determine the value or invoke the specified 440 * method. 441 */ 442 public abstract void invokeSetter(@NotNull Method method, 443 @NotNull Object object, 444 @NotNull Attribute attribute) 445 throws LDAPPersistException; 446}