All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cesecore.internal.UpgradeableDataHashMap Maven / Gradle / Ivy

/*************************************************************************
 *                                                                       *
 *  CESeCore: CE Security Core                                           *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.cesecore.internal;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.cesecore.util.Base64GetHashMap;



/**
 * UpgradeableDataHashMap is an class implementing the IUpgradeableData intended to be extended by
 * classes saving it's data to a database in BLOB/CLOB form.
 *
 * @version $Id: UpgradeableDataHashMap.java 27691 2017-12-29 14:24:12Z bastianf $
 *
 * @see org.cesecore.internal.IUpgradeableData
 */
public abstract class UpgradeableDataHashMap implements IUpgradeableData, Serializable {
    /**
     * Determines if a de-serialized file is compatible with this class.
     *
     * Maintainers must change this value if and only if the new version
     * of this class is not compatible with old versions. See Sun docs
     * for  details. 
     *
     */
	private static final long serialVersionUID = -1766329888474901945L;

    // Use LinkedHashMap because we want to have consistent serializing of the hashmap in order to be able to sign/verify data
    protected LinkedHashMap data;
    private boolean upgraded = false;
    public static final String VERSION = "version";

	/**
     * Creates a new UpgradeableDataHashMap object.
     */
    public UpgradeableDataHashMap() {
        data = new LinkedHashMap();
        data.put(VERSION, new Float(getLatestVersion()));
    }

    /**
     * @see IUpgradeableData#getLatestVersion()
     */
    @Override
    public abstract float getLatestVersion();

    /**
     * @see IUpgradeableData#getVersion()
     */
    @Override
    public float getVersion() {
        return ((Float) data.get(VERSION)).floatValue();
    }

    /**
     * @see IUpgradeableData#saveData()
     */
    @Override
    public Object saveData() {
        return data.clone();
    }

    public LinkedHashMap getRawData() {
        return data;
    }

    /**
     * @see IUpgradeableData#loadData(Object)
     */
    @Override
    @SuppressWarnings("unchecked")
    public void loadData(final Object data) {
    	// By creating a new LinkedHashMap (Base64GetHashMap) here we slip through a possible upgrade issue when upgrading
    	// from older implementation that used a plain HashMap instead.
    	// Both newer and older versions can be casted to HashMap.
    	this.data = new Base64GetHashMap((HashMap)data);
    	if(Float.compare(getLatestVersion(), getVersion()) > 0) {
    		upgrade();
    		upgraded = true;
    	}
    }

    /** So you can poll to see if the data has been upgraded
     *
     * @return true if data has been upgraded, false otherwise
     */
    public boolean isUpgraded() {
		return upgraded;
	}

    /**
     * Function that should handle the update if of the data in the class so it's up to date with
     * the latest version. An update is only done when needed.
     */
    @Override
    public abstract void upgrade();

    /** Create a Map with the differences between the current object and the parameter object.
     * Puts the result in a new Map with keys:
     * 
     * changed:key, changedvalue
     * remove:key, removedvalue
     * added:key, addedvalue
     * 
* * @param newobj The "changed" object for which we want to get the changes compared to this object * @return Map object with difference as described above */ public Map diff(UpgradeableDataHashMap newobj) { @SuppressWarnings("unchecked") Map newmap = (Map)newobj.saveData(); return diffMaps(data, newmap); } /** Create a Map with the differences between the two input objects. * Puts the result in a new Map with keys: *
     * changed:key, changedvalue
     * remove:key, removedvalue
     * added:key, addedvalue
     * 
* * @param oldmap * @param newmap * @return Map with difference */ public static Map diffMaps(Map oldmap, Map newmap) { Map result = new LinkedHashMap(); for (Object key : oldmap.keySet()) { if (newmap.containsKey(key)) { // Check if the value is the same Object value = oldmap.get(key); if (value == null) { if (newmap.get(key) != null) { result.put("addedvalue:"+key, newmap.get(key)); } } else if (!value.equals(newmap.get(key))) { Object val = newmap.get(key); if (val == null) { val = ""; } result.put("changed:"+key, getVal(val)); } } else { // Value removed Object val = oldmap.get(key); if (val == null) { val = ""; } result.put("removed:"+key, getVal(val)); } } // look for added properties for (Object key : newmap.keySet()) { if (!oldmap.containsKey(key)) { Object val = newmap.get(key); if (val == null) { val = ""; } result.put("added:"+key, getVal(val)); } } return result; } /** helper method to get nice output from types that do * not work nicely with Object.toString() */ private static String getVal(Object o) { StringBuilder b = new StringBuilder(); if (o instanceof String[]) { b.append('['); String[] arr = (String[]) o; for (String s: arr) { if (b.length() > 1) { b.append(", "); } b.append(s); } b.append(']'); } else { b.append(o); } return b.toString(); } // Helper methods for interacting with the stored data /** * Retrieve a boolean value from the data map in a safe manner. * @param key the key for the value to retrieve * @param defaultValue the default value to return if the value with the specified key does * not exist in the map or cannot be cast to a boolean * @return the value mapped to the key specified as first parameter or the default value * specified as second parameter if retrieval failed */ protected boolean getBoolean(final String key, final boolean defaultValue) { final Object object = data.get(key); if (object == null || !(object instanceof Boolean)) { return defaultValue; } return (Boolean) object; } /** * Retrieve a non-null string value from the data map in a safe manner. * @param key the key for the value to retrieve * @param defaultValue the default value to return if the value with the specified key does * not exist in the map or cannot be cast to a string * @return the value mapped to the key specified as first parameter or the default value * specified as second parameter if retrieval failed */ protected String getString(final String key, final String defaultValue) { final Object object = data.get(key); if (object == null || !(object instanceof String)) { return defaultValue; } return (String) object; } /** Set the value for the specified key as a primitive (never null) boolean */ protected void putBoolean(final String key, final boolean value) { data.put(key, Boolean.valueOf(value)); } /** * @return a deep copy of this hashmap's data object, for cloning purposes. */ protected LinkedHashMap getClonedData() { // We need to make a deep copy of the hashmap here LinkedHashMap clonedData = new LinkedHashMap<>(data.size()); for (final Entry entry : data.entrySet()) { Object value = entry.getValue(); if (value instanceof ArrayList) { // We need to make a clone of this object, but the stored immutables can still be referenced value = ((ArrayList)value).clone(); } clonedData.put(entry.getKey(), value); } return clonedData; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy