/*******************************************************************************
 * Copyright (c) 2015 MEDEVIT <office@medevit.at>.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     MEDEVIT <office@medevit.at> - initial API and implementation
 ******************************************************************************/
package info.elexis.server.core.connector.elexis.jpa.model.annotated.converter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Hashtable;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Converts the Elexis extInfo byte array into the contained Map<Object, Object>. 
 * The method conforms to the getInfoStore.
 *
 * This class requires, for compatibility reasons, the following (enclosed) classes<br> <br>
 * {@link ch.elexis.data.Kontakt}
 * 
 * @author M. Descher, MEDEVIT, Austria
 */
public class ElexisExtInfoMapConverter implements Converter {
	
	private static Logger log = LoggerFactory.getLogger(ElexisExtInfoMapConverter.class);
	
	private static final long serialVersionUID = -1939779538183350309L;

	@Override
	public void initialize(DatabaseMapping mapping, Session session){}
	
	@SuppressWarnings("unchecked")
	@Override
	public byte[] convertObjectValueToDataValue(Object objectValue, Session session){
		if(objectValue==null) {
			return null;
		}
		if (objectValue instanceof Hashtable) {
			Hashtable<Object, Object> ov = (Hashtable<Object, Object>) objectValue;
			return flatten(ov);
		}
		log.error("Could not convert into byte array "+objectValue);
		return null;
	}
	
	@Override
	public Map<Object, Object> convertDataValueToObjectValue(Object dataValue, Session session){
		if (dataValue == null) {
			return new Hashtable<Object, Object>();
		}
		Hashtable<Object, Object> ret = fold((byte[]) dataValue);
		if (ret == null) {
			return new Hashtable<Object, Object>();
		}
		return ret;
	}
	
	@Override
	public boolean isMutable(){
		return false;
	}
	
	/**
	 * Convert a Hashtable into a compressed byte array. Note: the resulting array is java-specific,
	 * but stable through jre Versions (serialVersionUID: 1421746759512286392L)
	 * 
	 * @param hash
	 *            the hashtable to store
	 * @return
	 */
	private static byte[] flatten(final Hashtable<Object, Object> hash){
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream(hash.size() * 30);
			ZipOutputStream zos = new ZipOutputStream(baos);
			zos.putNextEntry(new ZipEntry("hash"));
			ObjectOutputStream oos = new ObjectOutputStream(zos);
			oos.writeObject(hash);
			zos.close();
			baos.close();
			return baos.toByteArray();
		} catch (Exception ex) {
			log.warn("Exception flattening HashTable, returning null: "+ex.getMessage());
			return null;
		}
	}
	
	/**
	 * Recreate a Hashtable from a byte array as created by flatten()
	 * 
	 * @param flat
	 *            the byte array
	 * @return the original Hashtable or null if no Hashtable could be created from the array
	 */
	@SuppressWarnings("unchecked")
	private static Hashtable<Object, Object> fold(final byte[] flat) {
		Hashtable<Object, Object> res = null;
		if (flat.length == 0) {
			return res;
		}
		try {
			ByteArrayInputStream bais = new ByteArrayInputStream(flat);
			ZipInputStream zis = new ZipInputStream(bais);
			zis.getNextEntry();
			ObjectInputStream ois = new ObjectInputStream(zis);
			res = (Hashtable<Object, Object>) ois.readObject();
			ois.close();
			bais.close();
		} catch (IOException | ClassNotFoundException e) {
			log.warn("Exception folding byte array: " + e.getMessage());
		}
		return res;
	}
	
}

@@ -3684,48 +3684,8 @@
 ) %7B%0A
-%09%09Hashtable%3CObject, Object%3E res = null;%0A
 %09%09if
@@ -3716,19 +3716,20 @@
 %09return 
-res
+null
 ;%0A%09%09%7D%0A%09%09
@@ -3732,30 +3732,20 @@
 %7D%0A%09%09try 
-%7B%0A%09%09%09ByteArray
+(Zip
 InputStr
@@ -3748,18 +3748,17 @@
 tStream 
-ba
+z
 is = new
@@ -3750,32 +3750,51 @@
 tream zis = new 
+ZipInputStream(new 
 ByteArrayInputSt
@@ -3807,86 +3807,86 @@
 lat)
-;
+)) %7B
 %0A%09%09%09Zip
-InputStream zis = new ZipInputStream(bais);%0A%09%09%09zis.getNextEntry();%0A%09%09%09
+Entry entry = zis.getNextEntry();%0A%09%09%09if (entry != null) %7B%0A%09%09%09%09try (
 Obje
@@ -3935,18 +3935,23 @@
 zis)
-;%0A
+) %7B%0A%09%09
 %09%09%09re
-s =
+turn
  (Ha
@@ -3974,16 +3974,17 @@
 Object%3E)
+ 
  ois.rea
@@ -4001,37 +4001,44 @@
 %0A%09%09%09
-ois.close();%0A%09%09%09bais.close();
+%09%7D%0A%09%09%09%7D else %7B%0A%09%09%09%09return null;%0A%09%09%09%7D
 %0A%09%09%7D
@@ -4083,16 +4083,17 @@
 eption e
+x
 ) %7B%0A%09%09%09l
@@ -4087,36 +4087,37 @@
 on ex) %7B%0A%09%09%09log.
-warn
+error
 (%22Exception fold
@@ -4134,35 +4134,17 @@
 rray
-: %22 + e.getMessage()
+%22, ex
 );%0A%09
-%09%7D%0A
 %09%09re
@@ -4152,12 +4152,19 @@
 urn 
-res;
+null;%0A%09%09%7D%09%09
 %0A%09%7D%0A

Refactor fold operation