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

io.katharsis.jpa.internal.meta.impl.MetaDataObjectImpl Maven / Gradle / Ivy

There is a newer version: 3.0.2
Show newest version
package io.katharsis.jpa.internal.meta.impl;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtilsBean;

import io.katharsis.jpa.internal.meta.MetaAttribute;
import io.katharsis.jpa.internal.meta.MetaAttributeFinder;
import io.katharsis.jpa.internal.meta.MetaAttributePath;
import io.katharsis.jpa.internal.meta.MetaAttributeProjection;
import io.katharsis.jpa.internal.meta.MetaDataObject;
import io.katharsis.jpa.internal.meta.MetaKey;
import io.katharsis.jpa.internal.meta.MetaMapAttribute;
import io.katharsis.jpa.internal.meta.MetaMapType;
import io.katharsis.jpa.internal.meta.MetaProjection;
import io.katharsis.jpa.internal.meta.MetaType;
import io.katharsis.utils.PreconditionUtil;

public class MetaDataObjectImpl extends MetaTypeImpl implements MetaDataObject {

	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);
		}
	};

	private ArrayList subTypes = new ArrayList<>();
	private MetaDataObject superType;

	private HashMap attrMap = new HashMap<>();
	private List attrs = new ArrayList<>();
	private List declaredAttrs = new ArrayList<>();

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

	private MetaKey primaryKey;
	private Set keys = new HashSet<>();

	public MetaDataObjectImpl(Class implClass, Type implType, MetaDataObjectImpl superType) {
		super(null, implClass, implType);
		this.superType = superType;
		if (superType != null) {
			attrs.addAll(superType.getAttributes());
			attrMap.putAll(superType.attrMap);

			superType.addSubType(this);
		}

		initAttributes();
	}

	protected void initAttributes() {
		Class implClass = this.getImplementationClass();
		PropertyUtilsBean utils = BeanUtilsBean.getInstance().getPropertyUtils();
		PropertyDescriptor[] descriptors = utils.getPropertyDescriptors(implClass);
		for (PropertyDescriptor desc : descriptors) {
			if (desc.getReadMethod().getDeclaringClass() != implClass)
				continue; // contained in super type
			if (attrMap.containsKey(desc.getName()))
				throw new IllegalStateException(desc.toString());

			MetaAttributeImpl attr = newAttributeAttribute(this, desc);
			addAttribute(attr);
		}
	}

	protected void addAttribute(MetaAttributeImpl attr) {
		if (attrMap.containsKey(attr.getName()))
			throw new IllegalStateException(attr.toString());

		attrMap.put(attr.getName(), attr);
		attrs.add(attr);
		declaredAttrs.add(attr);
	}

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

	private void addSubType(MetaDataObjectImpl subType) {
		this.subTypes.add(subType);
		this.clearCache();
	}

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

	@Override
	public List getAttributes() {
		return attrs;
	}

	@Override
	public List getDeclaredAttributes() {
		return declaredAttrs;
	}

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

	@Override
	public String toString(Object entity) {
		boolean notFirst = false;
		StringBuilder b = new StringBuilder(entity.getClass().getSimpleName());
		b.append('[');

		for (MetaAttribute attr : getAttributes()) {
			if (attr.isAssociation()) {
				continue;
			}

			Object value = attr.getValue(entity);

			if (notFirst) {
				b.append(',');
			} else {
				notFirst = true;
			}
			b.append(attr.getName());
			b.append("=");
			b.append(formatValue(value));
		}
		b.append(']');
		return b.toString();
	}

	private Object formatValue(Object value) {
		if (value == null) {
			return "null";
		} else if (value instanceof Calendar) {
			Calendar cal = (Calendar) value;
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z");
			return sdf.format(cal.getTime());
		} else if (value instanceof Date) {
			Date cal = (Date) value;
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss Z");
			return sdf.format(cal.getTime());
		} else {
			return value.toString();
		}
	}

	protected MetaAttributeImpl newAttributeAttribute(MetaDataObjectImpl metaDataObject, PropertyDescriptor desc) {
		return new MetaAttributeImpl(metaDataObject, desc);
	}

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

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

	@Override
	public MetaAttributePath resolvePath(List attrPath, MetaAttributeFinder finder) {
		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;
				if (pathElement instanceof MetaAttributeProjection) {
					keyAttr = new MetaMapAttributeProjectionImpl(mapType, pathElement, keyString, true);
				} else {
					keyAttr = new MetaMapAttributeImpl(mapType, pathElement, keyString, true);
				}
				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();
		}
	}

	@Override
	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());
	}

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

	@Override
	public MetaDataObject getSuperType() {
		return superType;
	}

	@Override
	public MetaDataObject getRootType() {
		if (getSuperType() != null) // ensure proxy resolve
			return superType.getRootType();
		else
			return this;
	}

	@Override
	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;
	}

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

	@Override
	public List getSubTypes() {
		return subTypes;
	}

	private boolean isSuperTypeOf(MetaDataObject sub) {
		return this == sub || (sub != null && isSuperTypeOf(sub.getSuperType()));
	}

	@Override
	public MetaDataObject findSubTypeOrSelf(Class implClass) {
		if (implClass == null)
			throw new NullPointerException("class is null");
		Class localImplClass = getImplementationClass();
		if (implClass == localImplClass)
			return this;
		MetaDataObject subType = lookup.getMeta(implClass).asDataObject();
		if (isSuperTypeOf(subType))
			return subType;
		return null;
	}

	/**
	 * Gets the subtype with the given name (simple or qualitified).
	 * 
	 * @param name of the type
	 * @return meta object
	 */
	public MetaDataObject findSubTypeOrSelf(String name) {
		HashMap cache = subTypesMapCache;
		if (cache == null) {
			cache = new HashMap<>();
			List transitiveSubTypes = getSubTypes(true, true);
			for (MetaDataObject subType : transitiveSubTypes) {
				cache.put(subType.getName(), subType);
				cache.put(subType.getId(), subType);
			}
			subTypesMapCache = cache;
		}
		return subTypesMapCache.get(name);
	}

	@Override
	public MetaProjection asProjection() {
		if (this instanceof MetaProjection)
			return (MetaProjection) this;
		throw new IllegalStateException("not a projection");
	}

	@Override
	public MetaKey getPrimaryKey() {
		return primaryKey;
	}

	@Override
	public Set getKeys() {
		return keys;
	}

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

	public void addKey(MetaKey key) {
		keys.add(key);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy