001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009package imagingbook.common.util; 010 011import java.io.ByteArrayInputStream; 012import java.io.ByteArrayOutputStream; 013import java.io.IOException; 014import java.io.ObjectInputStream; 015import java.io.ObjectOutputStream; 016import java.lang.reflect.Field; 017import java.lang.reflect.InvocationTargetException; 018import java.util.ArrayList; 019import java.util.Base64; 020import java.util.List; 021 022/** 023 * Defines static methods for object copying and encoding to strings. 024 * 025 * @author WB 026 * @version 2022/09/11 027 */ 028public abstract class ObjectUtils { 029 030 private ObjectUtils() { 031 } 032 033 // https://stackoverflow.com/a/26000025 034 035 /** 036 * Returns a shallow copy of the given object using reflection. This can be used if the {@link Cloneable} interface 037 * is not (or cannot be) implemented. 038 * 039 * @param <T> generic type 040 * @param entity the object to be copied 041 * @return the object copy 042 */ 043 @SuppressWarnings("unchecked") 044 public static <T> T copy(T entity) { 045 Class<?> clazz = entity.getClass(); 046// T newEntity = (T) entity.getClass().newInstance(); 047 T newEntity = null; 048 try { 049 newEntity = (T) entity.getClass().getDeclaredConstructor().newInstance(); 050 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException 051 | NoSuchMethodException | SecurityException e) { } 052 053 while (clazz != null) { 054 copyFields(entity, newEntity, clazz); 055 clazz = clazz.getSuperclass(); 056 } 057 058 return newEntity; 059 } 060 061 private static <T> T copyFields(T entity, T newEntity, Class<?> clazz) { 062 List<Field> fields = new ArrayList<>(); 063 for (Field field : clazz.getDeclaredFields()) { 064 fields.add(field); 065 } 066 for (Field field : fields) { 067 field.setAccessible(true); 068 try { 069 field.set(newEntity, field.get(entity)); 070 } catch (IllegalArgumentException | IllegalAccessException e) { } 071 } 072 return newEntity; 073 } 074 075 // -------------------------------------------------------------------------------- 076 077 // adapted from https://stackoverflow.com/a/30968827 078 private static byte[] convertToBytes(Object object) { 079 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 080 try (ObjectOutputStream out = new ObjectOutputStream(bos)) { 081 out.writeObject(object); 082 return bos.toByteArray(); 083 } catch (IOException e) { } 084 return null; 085 } 086 087 private static Object convertFromBytes(byte[] bytes) { 088 ByteArrayInputStream bis = new ByteArrayInputStream(bytes); 089 try (ObjectInputStream in = new ObjectInputStream(bis)) { 090 return in.readObject(); 091 } catch (IOException | ClassNotFoundException e) { } 092 return null; 093 } 094 095 096 // TODO: check for null/exceptions 097 098 /** 099 * Serializes and encodes an arbitrary object to a string. This is potentially dangerous and only intended for local 100 * use, e.g., to define constant matrices with full precision. The string encoding is 'Base64' implemented with 101 * standard Java8 functionality. See {@link #decodeFromString(String)} for decoding. 102 * 103 * @param object an arbitrary object 104 * @return the Base64 string encoding of the object 105 */ 106 public static String encodeToString(Object object) { 107 byte[] ba = convertToBytes(object); 108 return Base64.getEncoder().encodeToString(ba); 109 } 110 111 // TODO: check for null/exceptions 112 113 /** 114 * Decodes and deserializes an object encoded with {@link #encodeToString(Object)}. The type of the encoded object 115 * must be known. 116 * 117 * @param string the encoded object string 118 * @return the associated object (cast to the known type) 119 */ 120 public static Object decodeFromString(String string) { 121 byte[] ba = Base64.getDecoder().decode(string); 122 return convertFromBytes(ba); 123 } 124 125}