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