package org.ovirt.engine.ui.uicommonweb.models.vms; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.ovirt.engine.core.common.queries.GetVmIconsParameters; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.ui.frontend.AsyncCallback; import org.ovirt.engine.ui.frontend.AsyncQuery; import org.ovirt.engine.ui.frontend.Frontend; /** * Bidirectional map Guid <-> String (icon id <-> dataUri icon) */ public class IconCache { protected GuidIconBiDiMap cache = new GuidIconBiDiMap(); private static class InstanceHolder { static IconCache instance = new IconCache(); } /** * Access is protected because of tests. */ protected IconCache() { } public static IconCache getInstance() { return InstanceHolder.instance; } /** * For test purpose only. */ protected static void setInstance(IconCache instance) { InstanceHolder.instance = instance; } /** * @param iconIds id of requested icon, should not be null, nor the item should be null * @param callback callback that receives the icon; The callback can be called both synchronously * (in case the icon is cached locally) and asynchronously (if a query to server * needs to be done) */ public void getOrFetchIcons(final List<Guid> iconIds, final IconsCallback callback) { assertNotNull(iconIds); final Map<Guid, String> localResult = getIcons(iconIds); if (localResult != null) { callback.onSuccess(localResult); } else { Frontend.getInstance().runQuery(VdcQueryType.GetVmIcons, GetVmIconsParameters.create(iconIds), new AsyncQuery<VdcQueryReturnValue>(returnValue -> { Map<Guid, String> idToIconMap = returnValue.getReturnValue(); IconCache.this.cache.putAll(idToIconMap); final Map<Guid, String> result = IconCache.this.getIcons(iconIds); callback.onSuccess(result); }) ); } } /** * @return cached icon or null if icon of given id is not cached */ public String getIcon(Guid iconId) { return cache.getIcon(iconId); } /** * Sugar for {@link #getOrFetchIcons(List, IconCache.IconsCallback)} */ public void getOrFetchIcon(final Guid iconId, final IconCallback callback) { getOrFetchIcons(Collections.singletonList(iconId), idToIconMap -> { final String icon = idToIconMap.get(iconId); callback.onSuccess(icon); }); } private void assertNotNull(List list) { if (list == null) { throw new IllegalArgumentException("Argument should not be null."); //$NON-NLS-1$ } for (Object item : list) { if (item == null) { throw new IllegalArgumentException("Argument should not contain null."); //$NON-NLS-1$ } } } /** * @param iconIds requested icon ids * @return icon ids -> icon data uri */ private Map<Guid, String> getIcons(List<Guid> iconIds) { Map<Guid, String> result = new HashMap<>(); for (Guid iconId : iconIds) { final String cachedIcon = cache.getIcon(iconId); if (cachedIcon == null) { return null; } result.put(iconId, cachedIcon); } return result; } public Guid getId(String icon) { return cache.getId(icon); } public interface IconsCallback extends AsyncCallback<Map<Guid, String>> { } public interface IconCallback extends AsyncCallback<String> { } private static class GuidIconBiDiMap { private Map<Guid, String> map = new HashMap<>(); private Map<String, Guid> reverseMap = new HashMap<>(); public String getIcon(Guid id) { return map.get(id); } public Guid getId(String icon) { return reverseMap.get(icon); } public void put(Guid id, String icon) { if (id == null || icon == null) { throw new IllegalArgumentException("Neither 'id' nor 'icon' can be null."); //$NON-NLS-1$ } map.put(id, icon); reverseMap.put(icon, id); } public void putAll(Map<Guid, String> idToIconMap) { for (Map.Entry<Guid, String> entry : idToIconMap.entrySet()) { put(entry.getKey(), entry.getValue()); } } } }