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

io.katharsis.meta.model.MetaDataObject Maven / Gradle / Ivy

There is a newer version: 3.0.2
Show newest version
package io.katharsis.meta.model;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonIgnore;

import io.katharsis.core.internal.utils.PreconditionUtil;
import io.katharsis.resource.annotations.JsonApiResource;
import io.katharsis.resource.annotations.JsonApiToMany;
import io.katharsis.resource.annotations.JsonApiToOne;

@JsonApiResource(type = "meta/dataObject")
public abstract class MetaDataObject extends MetaType {

	private static final MetaAttributeFinder DEFAULT_ATTRIBUTE_FINDER = new MetaAttributeFinder() {

		@Override
		public MetaAttribute getAttribute(MetaDataObject meta, String name) {
			return meta.getAttribute(name);
		}
	};

	private static final MetaAttributeFinder SUBTYPE_ATTRIBUTE_FINDER = new MetaAttributeFinder() {

		@Override
		public MetaAttribute getAttribute(MetaDataObject meta, String name) {
			return meta.findAttribute(name, true);
		}
	};
	
	@JsonApiToMany(opposite = "superType")
	private Set subTypes = new HashSet<>();

	@JsonApiToOne(opposite = "subTypes")
	private MetaDataObject superType;

	@JsonIgnore
	private Map attrMap = null;

	@JsonApiToMany
	private List attributes = null;

	@JsonApiToMany
	private List declaredAttributes = null;

	@SuppressWarnings("unchecked")
	@JsonIgnore
	private List[] subTypesCache = new List[4];

	@JsonApiToOne
	private MetaKey primaryKey;

	@JsonApiToMany
	private Set declaredKeys = new HashSet<>();

	@JsonApiToMany
	private Set interfaces = new HashSet<>();

	@JsonIgnore
	// TODO
	public MetaAttribute getVersionAttribute() {
		for (MetaAttribute attr : getAttributes()) {
			if (attr.isVersion())
				return attr;
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	private void clearCache() {
		subTypesCache = new List[4];
		attributes = null;
		declaredAttributes = null;
		attrMap = null;
	}

	public List getAttributes() {
		setupCache();
		return attributes;
	}

	public List getDeclaredAttributes() {
		setupCache();
		return declaredAttributes;
	}

	public MetaAttribute getAttribute(String name) {
		setupCache();
		MetaAttribute attr = attrMap.get(name);
		PreconditionUtil.assertNotNull(getName() + "." + name, attr);
		return attr;
	}

	public MetaAttributePath resolvePath(List attrPath, boolean includeSubTypes) {
		MetaAttributeFinder finder = includeSubTypes ? SUBTYPE_ATTRIBUTE_FINDER : DEFAULT_ATTRIBUTE_FINDER;
		return resolvePath(attrPath, finder);
	}

	public MetaAttributePath resolvePath(List attrPath) {
		return resolvePath(attrPath, true);
	}

	public MetaAttributePath resolvePath(List attrPath, MetaAttributeFinder finder) {
		setupCache();
		if (attrPath == null || attrPath.isEmpty())
			throw new IllegalArgumentException("invalid attribute path '" + attrPath + "'");
		LinkedList list = new LinkedList<>();

		MetaDataObject currentMdo = this;
		int i = 0;
		while (i < attrPath.size()) {
			String pathElementName = attrPath.get(i);
			MetaAttribute pathElement = finder.getAttribute(currentMdo, pathElementName);
			if (i < attrPath.size() - 1 && pathElement.getType() instanceof MetaMapType) {
				MetaMapType mapType = (MetaMapType) pathElement.getType();

				// next "attribute" is the key within the map
				String keyString = attrPath.get(i + 1);

				MetaMapAttribute keyAttr = new MetaMapAttribute(mapType, pathElement, keyString);
				list.add(keyAttr);
				i++;
				MetaType valueType = mapType.getValueType();
				currentMdo = nextPathElement(valueType, i, attrPath);
			} else {
				list.add(pathElement);
				currentMdo = nextPathElement(pathElement.getType(), i, attrPath);
			}
			i++;
		}

		return new MetaAttributePath(list);
	}

	private MetaDataObject nextPathElement(MetaType pathElementType, int i, List pathElements) {
		if (i == pathElements.size() - 1) {
			return null;
		} else {
			if (!(pathElementType instanceof MetaDataObject)) {
				throw new IllegalArgumentException("failed to resolve path " + pathElements);
			}
			return pathElementType.asDataObject();
		}
	}

	public MetaAttribute findAttribute(String name, boolean includeSubTypes) {
		if (hasAttribute(name)) {
			return getAttribute(name);
		}

		if (includeSubTypes) {
			List transitiveSubTypes = getSubTypes(true, true);
			for (MetaDataObject subType : transitiveSubTypes) {
				if (subType.hasAttribute(name)) {
					return subType.getAttribute(name);
				}
			}
		}

		throw new IllegalStateException("attribute " + name + " not found in " + getName());
	}

	public boolean hasAttribute(String name) {
		setupCache();
		return attrMap.containsKey(name);
	}

	public MetaDataObject getSuperType() {
		return superType;
	}

	public List getSubTypes(boolean transitive, boolean self) {
		int cacheIndex = (transitive ? 2 : 0) | (self ? 1 : 0);

		List cached = subTypesCache[cacheIndex];
		if (cached != null) {
			return cached;
		} else {
			ArrayList types = computeSubTypes(transitive, self);
			List unmodifiableList = Collections.unmodifiableList(types);
			subTypesCache[cacheIndex] = unmodifiableList;
			return unmodifiableList;
		}
	}

	private ArrayList computeSubTypes(boolean transitive, boolean self) {
		ArrayList types = new ArrayList<>();

		if (self && (!isAbstract() || !subTypes.isEmpty()))
			types.add(this);

		for (MetaDataObject subType : subTypes) {
			if (!subType.isAbstract() || !subType.getSubTypes().isEmpty())
				types.add(subType);
			if (transitive) {
				types.addAll(subType.getSubTypes(true, false));
			}
		}
		return types;
	}

	@JsonIgnore
	public boolean isAbstract() {
		return Modifier.isAbstract(getImplementationClass().getModifiers());
	}

	public Set getSubTypes() {
		return subTypes;
	}

	public Set getInterfaces() {
		return interfaces;
	}

	public void setInterfaces(Set interfaces) {
		this.interfaces = interfaces;
	}

	public MetaKey getPrimaryKey() {
		if (primaryKey == null && superType != null) {
			return superType.getPrimaryKey();
		}
		return primaryKey;
	}

	public Set getDeclaredKeys() {
		return declaredKeys;
	}

	public void setDeclaredKeys(Set declaredKeys) {
		this.declaredKeys = declaredKeys;
	}

	public void setDeclaredAttributes(List declaredAttributes) {
		this.declaredAttributes = declaredAttributes;
	}

	public void setAttributes(List attributes) {
		this.attributes = attributes;
	}

	public void setSubTypes(Set subTypes) {
		this.subTypes = subTypes;
	}

	public void setPrimaryKey(MetaKey key) {
		this.primaryKey = key;
		addDeclaredKey(key);
	}

	public void addDeclaredKey(MetaKey key) {
		declaredKeys.add(key);
	}

	public void addSubType(MetaDataObject subType) {
		this.subTypes.add(subType);
		this.clearCache();
	}

	public void setSuperType(MetaDataObject superType) {
		this.superType = superType;
		if (superType != null) {
			superType.addSubType(this);
		}
	}

	private void setupCache() {
		if (this.declaredAttributes == null) {
			List newDeclaredAttributes = new ArrayList<>();
			List newAttributes = new ArrayList<>();

			if (superType != null) {
				newAttributes.addAll(superType.getAttributes());
			}

			for (MetaElement child : getChildren()) {
				if (child instanceof MetaAttribute) {
					MetaAttribute attr = (MetaAttribute) child;
					newDeclaredAttributes.add(attr);
					newAttributes.add(attr);

				}
			}

			this.attributes = Collections.unmodifiableList(newAttributes);
			this.declaredAttributes = Collections.unmodifiableList(newDeclaredAttributes);
		}
		if (this.attrMap == null) {
			HashMap newAttrMap = new HashMap<>();
			if (superType != null) {
				newAttrMap.putAll(superType.attrMap);
			}
			for (MetaAttribute attr : declaredAttributes) {
				newAttrMap.put(attr.getName(), attr);
			}
			this.attrMap = Collections.unmodifiableMap(newAttrMap);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy