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

com.sap.cds.impl.CdsDataImpl Maven / Gradle / Ivy

/************************************************************************
 * © 2022-2023 SAP SE or an SAP affiliate company. All rights reserved. *
 ************************************************************************/
package com.sap.cds.impl;

import java.lang.reflect.Proxy;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.sap.cds.CdsData;
import com.sap.cds.impl.ProxyCreator.MapProxy;
import com.sap.cds.impl.parser.token.Jsonizer;
import com.sap.cds.util.DataUtils;

public class CdsDataImpl extends AbstractMap implements CdsData {

	private static final String FOR_REMOVAL = "forRemoval";
	private static final Map EMPTY_MAP = Map.of();

	protected final Map data;
	protected Map metadata;

	protected CdsDataImpl(Map data, Map metadata) {
		this.data = data;
		this.metadata = metadata;
	}

	protected CdsDataImpl(Map data) {
		this(data, EMPTY_MAP);
	}

	public static CdsData create(Map data) {
		if (data instanceof CdsData cdsData) {
			return cdsData;
		}
		return new CdsDataImpl(data);
	}

	public static CdsDataImpl copy(Map other) {
		Map metadata = metadataOf(other);
		return new CdsDataImpl(copyMap(other), metadata == EMPTY_MAP ? EMPTY_MAP : copyMap(metadata));
	}

	static Map metadataOf(Map other) {
		if (other instanceof CdsDataImpl otherCdsData) {
			return otherCdsData.metadata;
		} else if (other instanceof LazyRowImpl otherLazyRow) {
			return metadataOf(otherLazyRow.delegate());
		} else if (Proxy.isProxyClass(other.getClass())
				&& Proxy.getInvocationHandler(other) instanceof MapProxy otherProxy) {
			// unbox proxy and copy underlying data map
			return metadataOf(otherProxy.getCdsData());
		}
		return EMPTY_MAP;
	}

	private static Map copyMap(Map original) {
		return original.entrySet().stream().collect(CdsData::create,
				(m, e) -> m.put(e.getKey(), copyIfListOrMap(e.getValue())), CdsData::putAll);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static Object copyIfListOrMap(Object original) {
		if (original instanceof List list) {
			return CdsListImpl.copy(list);
		}
		if (original instanceof Map) {
			return CdsDataImpl.copy((Map) original);
		}
		return original; // expected to be immutable
	}

	@Override
	@SuppressWarnings("unchecked")
	public  T getMetadata(String key) {
		return (T) metadata.get(key);
	}

	@Override
	@SuppressWarnings("unchecked")
	public  T putMetadata(String key, T value) {
		if (metadata == EMPTY_MAP) {
			// not thread-safe, but assumed to be okay
			metadata = new HashMap<>();
		}
		return (T) metadata.put(key, value);
	}

	@Override
	@SuppressWarnings("unchecked")
	public CdsData forRemoval(boolean remove) {
		putMetadata(FOR_REMOVAL, remove);
		return this;
	}

	@Override
	public boolean isForRemoval() {
		return Boolean.TRUE.equals(getMetadata(FOR_REMOVAL));
	}

	@Override
	public boolean containsPath(String path) {
		return DataUtils.containsKey(data, path);
	}

	@Override
	public Set> entrySet() {
		return data.entrySet();
	}

	@Override
	public Object put(String key, Object value) {
		return data.put(key, value);
	}

	@Override
	public  T removePath(String path) {
		@SuppressWarnings("unchecked")
		T value = (T) DataUtils.removePath(data, path, true);

		return decorateMap(value);
	}

	@Override
	public  T putPath(String path, T value) {
		@SuppressWarnings("unchecked")
		T oldValue = (T) DataUtils.putPath(data, path, value);

		return decorateMap(oldValue);
	}

	@Override
	public  T putPathIfAbsent(String path, T value) {
		T v = getPath(path);
		if (v == null) {
			putPath(path, value);
		}
		return v;
	}

	@Override
	public Object get(Object key) {
		Object value = data.get(key);

		return decorateMap(value);
	}

	@Override
	public  T getPath(String path) {
		return getPathOrDefault(path, null);
	}

	@Override
	public  T getPathOrDefault(String path, T defaultValue) {
		T value = DataUtils.getPathOrDefault(data, path, defaultValue);

		return decorateMap(value);
	}

	@SuppressWarnings("unchecked")
	private  T decorateMap(T value) {
		if (value instanceof Map map) {
			return (T) decorate(map);
		}
		return value;
	}

	protected CdsData decorate(Map map) {
		if (map instanceof CdsData data) {
			return data;
		}
		return new CdsDataImpl(map);
	}

	@Override
	public String toJson() {
		return Jsonizer.json(this);
	}

	public static class Factory implements CdsData.Factory {
		@Override
		public CdsData create() {
			return new CdsDataImpl(new HashMap<>());
		}

		@Override
		public CdsData create(Map data) {
			return CdsDataImpl.create(data);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy