view src/main/java/org/mpi/openmind/cache/WrapperService.java @ 73:ab61cd3ad0e0

cleanup.
author casties
date Thu, 02 Feb 2017 19:02:05 +0100
parents aeb29e362a67
children 68e3e4b569f9
line wrap: on
line source

package org.mpi.openmind.cache;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.mpi.openmind.cache.CacheService.EntityDiff;
import org.mpi.openmind.repository.bo.Attribute;
import org.mpi.openmind.repository.bo.Entity;
import org.mpi.openmind.repository.bo.Node;
import org.mpi.openmind.repository.bo.Relation;
import org.mpi.openmind.repository.bo.ViewerAttribute;
import org.mpi.openmind.repository.bo.ViewerPage;
import org.mpi.openmind.repository.bo.utils.EntitySortByNormalizedOwnValue;
import org.mpi.openmind.repository.bo.utils.EntitySortByVersion;
import org.mpi.openmind.repository.bo.utils.RelationSortBySourceOW;
import org.mpi.openmind.repository.bo.utils.RelationSortByTargetOW;
import org.mpi.openmind.repository.services.PersistenceService;
import org.mpi.openmind.repository.services.utils.AttributeFilter;
import org.mpi.openmind.repository.services.utils.EditIntent;
import org.mpi.openmind.repository.utils.ImportOM3Util;
import org.mpi.openmind.repository.utils.NormalizerUtils;
import org.mpi.openmind.repository.utils.RomanizationLoC;

public class WrapperService implements Serializable{

	private static final long serialVersionUID = 2578074607841626396L;

	public static final String IS_TYPE_OF = "is_type_of";

	private static Logger logger = Logger.getLogger(WrapperService.class);

    private static Logger txLog = Logger.getLogger(PersistenceService.TRANSACTION_LOGGER);

    private transient CacheService cache;

	public long getSourceRelationsCount(Entity entity, String relationName,
			String tarObjClass) {
		return this.getPS().getSourceRelationsCount(entity, relationName,
				tarObjClass);
	}

	public Long getTargetRelationsCount(Entity entity, String relationName,
			String srcObjClass) {
		return this.getPS().getTargetRelationsCount(entity, relationName,
				srcObjClass);
	}

	/**
	 * Search entities matching a list of attribute filters.
	 * 
	 * Returns a Map of Entities and Attributes.
	 * 
	 * @param filters
	 * @param maxResult
	 * @return
	 */
	public Map<Entity, Attribute> searchEntityByAttributeFilter (
			List<AttributeFilter> filters, int maxResult) {
		return this.getPS().searchEntityByAttributeFilter(filters, maxResult);
	}

	public Map<Attribute, Entity> searchAttEntityByAttributeFilter(
			List<AttributeFilter> filters, int maxResult) {
		return this.getPS()
				.searchAttEntityByAttributeFilter(filters, maxResult);
	}

	public List<Entity> searchEntityByOwnValue(String oc, String term){
		List<Entity> rs = new ArrayList<Entity>();
		if(StringUtils.isEmpty(term))
			return rs;
		
		List<Entity> entList = this.cache.loadEntitiesByDef(oc);
		term = NormalizerUtils.normalize(term);
		
		for(Entity ent : entList){
			if(StringUtils.isNotEmpty(ent.getNormalizedOwnValue()) &&
					StringUtils.contains(ent.getNormalizedOwnValue(), term)){
				rs.add(ent);
			}
		}
		return rs;
	}
	
	// ************************************************************
	// ************************************************************
	// ************************************************************
	// ************************************************************

	public List<Entity> getPreviousEntitiesById(Long id) {
		List<Entity> list = this.getPS().getEntities(id,
				Node.SYS_STATUS_PREVIOUS_VERSION, null, null);
		Collections.sort(list, new EntitySortByVersion());
		return list;
	}

	public void initCache() {
		logger.info("##### Initializing Cache #####");
		logger.info(ManagementFactory.getRuntimeMXBean().getName());

		try {
			int mb = 1024 * 1024;

			// Getting the runtime reference from system
			Runtime runtime = Runtime.getRuntime();

			logger.info("##### Heap utilization statistics [MB] #####");

			// Print used memory
			logger.info("Used Memory:"
					+ (runtime.totalMemory() - runtime.freeMemory()) / mb);

			// Print free memory
			logger.info("Free Memory:" + runtime.freeMemory() / mb);

			// Print total available memory
			logger.info("Total Memory:" + runtime.totalMemory() / mb);

			// Print Maximum available memory
			logger.info("Max Memory:" + runtime.maxMemory() / mb + "\n");
		} catch (Exception e) {
		    logger.error(e);
		}

		if (!this.cache.areDefsLoaded()) {
			try {
				this.cache.initDefinitions(getPS().getLWDefinitions(), 
				        getPS().getDefAttributes(), 
				        getPS().getDefRelations());
			} catch (Exception e) {
				logger.error(e);
			}
		}
	}

	public void removeCurrentVersionEntity(Entity entity, String user) throws Exception {
		logger.info("removeCurrentVersionEntity ("+ user + ") " + entity);
        txLog.debug("** START remove entity: user="+user+" entity="+entity.toSmallString());
        // delete entity from cache
		this.cache.deleteEntity(entity.getId(), entity.getObjectClass());
		// delete entity from DB
		this.getPS().removeEntCurrentVersion(entity.getId(), entity.getType());
        txLog.debug("** END remove entity: user="+user+" entity="+entity.toSmallString());
	}

	public Map<Entity, Attribute> searchEntityByAttributeFilter(String term, List<AttributeFilter> filters,
			int maxResults) {
		Map<Entity, Attribute> map = new HashMap<Entity, Attribute>();
		List<Long> usedIds = new ArrayList<Long>();

		boolean mustBreak = false;
		int count = 0;
		if (StringUtils.isNotEmpty(term)) {
			String normalizedTerm = NormalizerUtils.normalize(term);
			for (AttributeFilter filter : filters) {
				if (mustBreak) {
					break;
				}
				for (Attribute att : getAttributesByDefByAttName(filter.getEntObjectClass(), filter.getName(), -1)) {
					if (!usedIds.contains(att.getSourceId()) && StringUtils.isNotEmpty(att.getNormalizedOwnValue())
							&& att.getNormalizedOwnValue().contains(normalizedTerm)) {
						map.put(getEntityById(att.getSourceId()), att);
						usedIds.add(att.getSourceId());
						count++;
						if (maxResults > 0 && count == maxResults) {
							mustBreak = true;
							break;
						}
					}
				}
			}
		}
		return map;
	}

	public int getEntitiesCount(String def) {
		int count = this.cache.getEntitiesCount(def);
		return count;
	}

    /**
     * Returns a List of Entities of the given definition (object_class).
     * 
     * All Entities are sorted by normalized own-value first. Then the
     * part between fromIndex and toIndex is returned.
     * 
     * @param def
     * @param fromIndex
     * @param toIndex
     * @return
     */
    public List<Entity> getEntityByDefSubList(String def, int fromIndex, int toIndex) {
        List<Entity> list = new ArrayList<Entity>();
        List<Entity> all = getEntitiesByDef(def);
        if (all != null && all.size() >= toIndex) {

            long start = System.currentTimeMillis();
            Collections.sort(list, new EntitySortByNormalizedOwnValue());
            long diff = System.currentTimeMillis() - start;
            logger.debug("Sorting entities time[ms] " + diff);

            for (Entity ent : all.subList(fromIndex, toIndex)) {
                list.add(ent);
            }
        }
        return list;
    }

	/**
	 * Returns a List of all Entities of the given definition (object_class).
	 * 
	 * @param def
	 * @return
	 */
	public List<Entity> getEntitiesByDef(String def) {
		List<Entity> list = this.cache.loadEntitiesByDef(def);
		return list;
	}

	public List<Entity> getEntityByDefAndOW(String def, String ow,
			int maxResults) {
		List<Entity> result = new ArrayList<Entity>();
		if (StringUtils.isNotEmpty(ow)) {
			List<Entity> list = this.cache.loadEntitiesByDef(def);

			int count = 0;
			ow = NormalizerUtils.normalize(ow);
			for (Entity e : list) {
				// String eow = (StringUtils.isNotEmpty(e.getOwnValue())) ?
				// e.getOwnValue().toLowerCase() : "";
				if (StringUtils.isNotBlank(e.getNormalizedOwnValue())
						&& e.getNormalizedOwnValue().contains(ow)) {
					result.add((Entity) e.clone());
					count++;
				}
				
				if (maxResults > 0 && maxResults == count) {
					break;
				}
			}
		}
		return result;
	}

	/**
	 * this method does not execute: - ownValue generation - new version
	 * generation This method only save the entity in the current state.
	 * 
	 * @param nodeList
	 * @param user
	 */
	public void saveEntityListAsNode(List<Entity> entList, String user) {
		for (Entity ent : entList) {
			ent.setUser(user);
		}
		getPS().saveEntityListAsNode(entList);
		for (Entity ent : entList) {
			cache.saveEntity(ent);
		}
	}

	public void saveEntityListAsNodeWithoutContent(List<Entity> nodeList,
			String user) throws Exception {
		this.cache.saveEntityListAsNodeWithoutContent(nodeList);
	}

	public boolean existEntity(Long entId) {
		return getCache().getEntityByIdReadOnly(entId) != null;
	}

	public boolean existRelation(Long srcId, Long tarId, String relName) {

		try {
			if (srcId == null || tarId == null) {
				throw new Exception("srcId and tarId can not be null.");
			}

			List<Relation> relList = this.cache.getRelsBySrcId(srcId);

			for (Relation rel : relList) {
				if (rel.getTargetId().equals(tarId)
						&& rel.getOwnValue().equals(relName))
					return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return false;
	}

	public void saveRelationAsNode(Relation rel, String user) throws Exception {

		if (rel.getAttributes() != null && rel.getAttributes().size() > 0) {
			throw new Exception(
					"This method can be only used if the relation does not contains attributes, this is not the case.");
		}

		rel.setUser(user);
		this.getPS().saveNode(rel);
		this.cache.updateRelationAsNode(rel);
	}

	public List<Entity> searchEntityByAttributeOfTarRelation(String objClass,
			String relName, String objClassSrc, String attName,
			String attValue, int maxResults) throws Exception {
		List<Entity> list = new ArrayList<Entity>();

		List<Attribute> attList = getAttributesByDefByAttName(objClassSrc,
				attName, attValue, -1);
		int count = 0;
		for (Attribute att : attList) {
			Relation rel = getFirstRelationByTargetOCByName(
					cache.getRelsBySrcId(att.getSourceId()), objClass, relName);
			if (rel != null) {
				list.add(getEntityById(rel.getTargetId()));
				count++;
				if (maxResults > 0 && maxResults == count) {
					break;
				}
			}
		}

		return list;
	}

	private static Relation getFirstRelationByTargetOCByName(
			List<Relation> list, String tarOC, String name) {
		if (StringUtils.isNotEmpty(name)) {
			name = name.toLowerCase();
			for (Relation rel : list) {
				if (rel.getTargetObjectClass().equals(tarOC)) {
					if (StringUtils.isNotEmpty(rel.getOwnValue())
							&& name.equals(rel.getOwnValue().toLowerCase())) {
						return rel;
					}
				}
			}
		}
		return null;
	}

	public List<Attribute> getAttributeByEntId(Long entId) {
		List<Attribute> list = cache.getAttsBySrcId(entId);
		return list;
	}

	public Attribute getAttributeByName(Long entId, String name) {
		List<Attribute> list = cache.getAttsBySrcId(entId);

		for (Attribute att : list) {
			if (att.getName().equals(name)) {
				return (Attribute) att.clone();
			}
		}
		return null;
	}

	public String getAttributeOVByName(Long entId, String name, boolean useRomanization){
		Attribute att = getAttributeByName(entId, name);
		if(att != null){
			return (useRomanization) ? RomanizationLoC.convert(att.getOwnValue()) : att.getOwnValue();
		}
		return "";
	}
	
	public List<Attribute> searchAttribute(String firstName, String firstValue,
			String secondName, String secondValue, String def, int maxResults) {
		List<Attribute> list = new ArrayList<Attribute>();

		List<Attribute> firstAttList = cache.getAttsByOCAndName(def, firstName);

		firstValue = StringUtils.isNotEmpty(firstValue) ? NormalizerUtils
				.normalize(firstValue) : null;
		secondValue = StringUtils.isNotEmpty(secondValue) ? NormalizerUtils
				.normalize(secondValue) : null;

		if (StringUtils.isNotEmpty(firstValue)) {
			for (Attribute firstAtt : firstAttList) {
				String attValue1 = (StringUtils.isNotEmpty(firstAtt
						.getNormalizedOwnValue())) ? firstAtt
						.getNormalizedOwnValue() : "";
				// (StringUtils.isNotEmpty(firstAtt.getValue())) ?
				// firstAtt.getValue().toLowerCase() : "";
				if (StringUtils.isNotEmpty(firstValue)
						&& attValue1.contains(firstValue)) {
					Attribute secondAtt = getAttributeByName(
							firstAtt.getSourceId(), secondName);
					if (secondAtt != null) {
						String attValue2 = (StringUtils.isNotEmpty(secondAtt
								.getNormalizedOwnValue())) ? secondAtt
								.getNormalizedOwnValue() : "";
						// (StringUtils.isNotEmpty(secondAtt.getValue())) ?
						// secondAtt.getValue().toLowerCase() : "";
						if (StringUtils.isNotEmpty(secondValue)
								&& attValue2.contains(attValue2)) {
							list.add((Attribute) firstAtt.clone());
						}
					}
				}
			}
		}

		return list;
	}

	private List<Attribute> getAttributesByDefByAttName(String def,
			String attName, int maxResults) {
		List<Attribute> list = cache.getAttsByOCAndName(def, attName);
		return list;
	}

	/**
	 * Returns a sorted list of attributes by def (=objectClass) of the entity, the att Name
	 * and its (normalized) value.
	 * 
	 * @param def
	 * @param attName
	 * @param attValue
	 *            case insensitive sub string match.
	 * @param maxResults
	 * @return
	 */
	public List<Attribute> getAttributesByDefByAttName(String def,
			String attName, String attValue, int maxResults) {
		
		List<Attribute> list = getAttributesByDefByAttName0(def, attName, attValue, maxResults);
		Collections.sort(list);
		return list;
	}
	
	public List<Attribute> getAttributes(String oc, String attName){
		return cache.getAttsByOCAndName(oc, attName);
	}
	
	/**
	 * 
	 * @param attName
	 * @param attValue
	 * @return
	 */
	public List<Attribute> getAttributesByExactValue(String attName, String attValue){
		List<Attribute> list = new ArrayList<Attribute>();
		
		if(StringUtils.isNotEmpty(attValue)){
			for(Entity def : cache.getLWDefinitions()){
				
				List<Attribute> attList = cache.getAttsByOCAndName(def.getOwnValue(), attName);
				attValue = NormalizerUtils.normalize(attValue);
					
				for(Attribute att : attList){
					if(StringUtils.equals(attValue, att.getNormalizedOwnValue())){
						list.add(att);
					}
				}
				
			}
			Collections.sort(list);	
		}
		return list;
	}
	
	/**
     * Returns a list of attributes by def (=objectClass) of the entity, the att Name
     * and its (normalized) value.
     * 
	 * @param def
	 * @param attName
	 * @param attValue
	 * @param maxResults
	 * @return
	 */
	private List<Attribute> getAttributesByDefByAttName0(String def,
			String attName, String attValue, int maxResults) {
		List<Attribute> list = new ArrayList<Attribute>();
		List<Attribute> attList = cache.getAttsByOCAndName(def, attName);
		attValue = NormalizerUtils.normalize(attValue);
		int count = 0;
		if (StringUtils.isEmpty(attValue)) {
			for (Attribute att : attList) {
				list.add((Attribute) att.clone());
				count++;
				if (maxResults > 0 && maxResults == count) {
					break;
				}
			}
		} else {
			for (Attribute att : attList) {
				String attValue0 = StringUtils.isNotEmpty(att
						.getNormalizedOwnValue()) ? att.getNormalizedOwnValue()
						: "";
				if (StringUtils.isNotEmpty(attValue)
						&& attValue0.contains(attValue)) {
					list.add((Attribute) att.clone());
					count++;
					if (maxResults > 0 && maxResults == count) {
						break;
					}
				}
			}
		}
		return list;
	}

	/**
	 * Returns a list of entities.
	 * 
	 * @param def
	 * @param attName
	 * @param attValue
	 *            ignores case sensitive
	 * @param maxResults
	 * @param subString
	 *            if true the attValue is searching as substring
	 * @return
	 */
	public List<Entity> getEntitiesByAtt(String def, String attName,
			String attValue, int maxResults, boolean subString) {
		List<Entity> list = new ArrayList<Entity>();

		List<Attribute> attList = cache.getAttsByOCAndName(def, attName);

		int count = 0;
		if (StringUtils.isEmpty(attValue)) {
			for (Attribute att : attList) {
				list.add(getEntityById(att.getSourceId()));
				count++;
				if (maxResults > 0 && maxResults == count) {
					break;
				}
			}
		} else {
			attValue = (StringUtils.isNotEmpty(attValue)) ? NormalizerUtils
					.normalize(attValue) : "";
			for (Attribute att : attList) {
				String attValue0 = (StringUtils.isNotEmpty(att
						.getNormalizedOwnValue())) ? att
						.getNormalizedOwnValue() : "";
				if ((subString && attValue0.contains(attValue))
						|| (!subString && attValue.equals(attValue0))) {
					list.add(getEntityById(att.getSourceId()));
					count++;
					if (maxResults > 0 && maxResults == count) {
						break;
					}
				}
			}
		}
		return list;
	}

	public void removeDefAttribute(Attribute att) throws Exception {
		this.cache.deleteDefAttribute(att);
		att.setSystemStatus(Node.SYS_STATUS_PREVIOUS_VERSION);
		this.getPS().saveNode(att);
	}

	public void removeDefRelation(Relation rel) throws Exception {
		this.cache.deleteDefRelation(rel);
		rel.setSystemStatus(Node.SYS_STATUS_PREVIOUS_VERSION);
		this.getPS().saveNode(rel);
	}

	public Entity saveLWDefinition(Entity def, String user) throws Exception {
		boolean isNew = !def.isPersistent();
		def.setUser(user);
		def.increaseVersion();
		def.setModificationTime(System.currentTimeMillis());
		def.setType(Node.TYPE_TBOX);
		def.setObjectClass(Node.TYPE_TBOX);
		def.setSystemStatus(Node.SYS_STATUS_CURRENT_VERSION);
		this.getPS().saveNode(def);

		if (isNew) {
			this.cache.createLWDefinition(def);
		} else {
			this.cache.saveLWDefinition(def);
		}

		return def;
	}

	public Attribute saveDefAttribute(Attribute att, String user)
			throws Exception {
		att.setUser(user);
		att.increaseVersion();
		att.setModificationTime(System.currentTimeMillis());
		att.setObjectClass(Node.TYPE_TBOX);
		att.setType(Node.TYPE_TBOX);
		att.getPossibleValues();
		this.getPS().saveNode(att);
		this.cache.saveDefAttribute(att);
		return att;
	}

	public Relation saveDefRelation(Relation rel, String user) throws Exception {
		rel.setUser(user);
		rel.increaseVersion();
		rel.setModificationTime(System.currentTimeMillis());
		rel.setObjectClass(Node.TYPE_TBOX);
		rel.setType(Node.TYPE_TBOX);
		this.getPS().saveNode(rel);
		this.cache.saveDefRelation(rel);
		return rel;
	}

	/**
	 * Save the given entity as a separate new entity with a new ID.
	 * 
	 * Keeps attributes and relations as copies.
	 * 
	 * @param entity
	 * @param user
	 * @return
	 * @throws Exception
	 */
	public Entity saveEntityAsNew(Entity entity, String user) throws Exception {
		long start = System.currentTimeMillis();
		entity.resetRowId();
		entity.resetId();
		entity.setType(Node.TYPE_ABOX);
		entity.setUser(user);
		
		txLog.debug("** START save entity as new: user="+user+" entity="+entity.toSmallString());
		// save in database
		this.getPS().saveEntity(entity);
		// save in cache
		cache.saveEntity(entity);
        txLog.debug("** END save entity as new: user="+user+" entity="+entity.toSmallString());
		
		logger.debug("[U=" + user + "] SaveEntityAsNew - execution time[ms]: " + (System.currentTimeMillis() - start));
		return (Entity) entity.clone();
	}

	/**
     * Save the given entity to the database.
     * 
     * Creates a new version of the entity.
     * 
	 * @param entity
	 * @param user
	 * @param intent TODO
	 * @return
	 * @throws Exception
	 */
	public Entity saveEntity(Entity entity, String user, EditIntent intent) throws Exception {
		long start = System.currentTimeMillis();
		if (StringUtils.isEmpty(entity.getType())) {
			entity.setType(Node.TYPE_ABOX);
		}
		entity.setUser(user);

		entity = removeWrongRelations(entity);
		entity = removeEmptyAttributes(entity);

		txLog.debug("** START save entity: user="+user+" entity="+entity.toSmallString());
		// check if entity is up to date with cache
		if (!cache.isEntityCurrent(entity)) {
			logger.warn("Possible collision: Entity to save not current!");
			// create differences from cache
			EntityDiff diff = cache.diffEntityCache(entity, true);
			if (diff != null) {
				if (intent != null) {
					/*
					 *  check if diff was intended
					 */
					if (!intent.isEntModificationIntended(entity.getObjectClass())) {
						logger.error("unintended modification of entity: "+entity);
					}
					for (Attribute a : diff.addedAttributes) {
						if (!intent.isAttModificationIntended(a.getName())) {
							logger.error("unintended addition: "+a);
						}
					}
					for (Attribute a : diff.modifiedAttributes) {
						if (!intent.isAttModificationIntended(a.getName())) {
							logger.error("unintended modification: "+a);
						}
					}
					for (Attribute a : diff.removedAttributes) {
						if (!intent.isAttModificationIntended(a.getName())) {
							logger.error("unintended removal prevented: "+a);
							entity.addAttribute(a);
						}
					}
					for (Relation r : diff.addedSrcRels) {
						if (!intent.isSrcRelModificationIntended(r.getObjectClass())) {
							logger.error("unintended addition: "+r);
						}
					}
					for (Relation r : diff.modifiedSrcRels) {
						if (!intent.isSrcRelModificationIntended(r.getObjectClass())) {
							logger.error("unintended modification: "+r);
						}
					}
					for (Relation r : diff.removedSrcRels) {
						if (!intent.isSrcRelModificationIntended(r.getObjectClass())) {
							logger.error("unintended removal prevented: "+r);
							// re-add
							entity.addSourceRelation(r);
						}
					}
					for (Relation r : diff.addedTarRels) {
						if (!intent.isTarRelModificationIntended(r.getObjectClass())) {
							logger.error("unintended addition: "+r);
						}
					}
					for (Relation r : diff.modifiedTarRels) {
						if (!intent.isTarRelModificationIntended(r.getObjectClass())) {
							logger.error("unintended modification: "+r);
						}
					}
					for (Relation r : diff.removedTarRels) {
						if (!intent.isTarRelModificationIntended(r.getObjectClass())) {
							logger.error("unintended removal prevented: "+r);
							// re-add
							entity.addTargetRelation(r);
						}
					}
				} else {
					logger.warn("Save with missing EditIntent!");
				}
			} else {
				logger.warn("No difference to cache found.");
			}
		}
		
		// save in database
		this.getPS().saveEntity(entity);
		// save in cache
		cache.saveEntity(entity);
		txLog.debug("** END save entity: user="+user+" entity="+entity.toSmallString());
	
		logger.debug("[U=" + user + "] SaveEntity - execution time[ms]: " + (System.currentTimeMillis() - start));
		Entity clone = (Entity) entity.clone();
		return clone;
	}
	
	public void saveEntityList(List<Entity> list, String user) throws Exception{
		long start = System.currentTimeMillis();
		for(Entity entity : list){
			if (StringUtils.isEmpty(entity.getType())) {
				entity.setType(Node.TYPE_ABOX);
			}
			entity.setUser(user);

			entity = removeWrongRelations(entity);
			entity = removeEmptyAttributes(entity);
		}
        txLog.debug("** START save entity list: user="+user+" list="+list);
		this.getPS().saveEntityList(list);
		for(Entity entity : list){
			cache.saveEntity(entity);
		}
        txLog.debug("** END save entity list: user="+user+" list="+list);
		logger.debug("[U=" + user + "] SaveEntityList - execution time[ms]: " + (System.currentTimeMillis() - start));
	}
	
	private Entity removeEmptyAttributes(Entity ent){
		
		for(Attribute att : new ArrayList<Attribute>(ent.getAttributes())){
			if(StringUtils.isEmpty(att.getValue())){
				ent.getAttributes().remove(att);
			}
		}
		return ent;
		
	}

    /**
     * Returns if this entity has wrong relations.
     * 
     * Wrong are source relations whose target doesn't exist.
     * Similarly for target relations.
     * 
     * @param ent
     * @return
     */
	private boolean hasWrongRelations(Entity ent) {
		for (Relation srcRel : ent.getSourceRelations()) {
			if (getEntityById(srcRel.getTargetId()) == null) {
				return true;
			}
		}

		for (Relation tarRel : ent.getTargetRelations()) {
			if (getEntityById(tarRel.getSourceId()) == null) {
				return true;
			}
		}
		return false;
	}

    /**
     * Remove wrong relations from this entity.
     * 
     * Wrong are source relations whose target is null or doesn't exist.
     * Similarly for target relations.
     * 
     * @param ent
     * @return
     */
    private Entity removeWrongRelations(Entity ent) {

        for (Relation srcRel : new ArrayList<Relation>(ent.getSourceRelations())) {
            if (srcRel.getTargetId() == null || getEntityByIdReadOnly(srcRel.getTargetId()) == null) {
                ent.getSourceRelations().remove(srcRel);
                logger.error("Inconsistency detected saving entity [" + ent.getId() + "] " + srcRel.toString());
            }
        }

        for (Relation tarRel : new ArrayList<Relation>(ent.getTargetRelations())) {
            if (tarRel.getSourceId() == null || getEntityByIdReadOnly(tarRel.getSourceId()) == null) {
                ent.getTargetRelations().remove(tarRel);
                logger.error("Inconsistency detected saving entity [" + ent.getId() + "] " + tarRel.toString());
            }
        }

        return ent;
    }

	public Attribute getDefAttributeByOwnValue(String defOC, String attOW) {
		for (Attribute att : this.cache.getDefAttributes(defOC)) {
			if (att.getOwnValue().equals(attOW)) {
				return att;
			}
		}
		return null;
	}

	public List<Attribute> getDefRelationAttributes(Long id) {
		return this.cache.getDefAttributesById(id);
	}
	
    /**
     * Returns the attributes of the definition of the given type.
     * 
     * @param objectClass
     * @return
     */
	public List<Attribute> getDefAttributes(String objectClass) {
		return this.cache.getDefAttributes(objectClass);
	}

	/**
	 * Returns the source relations of the definition of the given type.
	 * 
	 * @param objectClass
	 * @return
	 */
	public List<Relation> getDefSourceRelations(String objectClass) {
		return this.cache.getDefSourceRelations(objectClass);
	}

    /**
     * Returns the target relations of the definition of the given type.
     * 
     * @param objectClass
     * @return
     */
	public List<Relation> getDefTargetRelations(String objectClass) {
		return this.cache.getDefTargetRelations(objectClass);
	}

	public Entity getDefinitionById(Long id) {
		return this.cache.getLWDefinitionById(id);
	}

    /**
     * Returns all (lightweight) definition entities.
     * 
     * @return
     */
	public List<Entity> getLWDefinitions() {
		return this.cache.getLWDefinitions();
	}

    /**
     * Returns the (lightweight) definition entity for the given type.
     * 
     * @param objectClass
     * @return
     */
	public Entity getDefinition(String objectClass) {
		return this.cache.getLWDefinition(objectClass);
	}

	public List<Relation> getRelation(String name, String srcOC, String tarOC)
			throws Exception {
		// TODO read only???
		List<Relation> list = new ArrayList<Relation>();
		if (StringUtils.isNotEmpty(name)) {
			List<Relation> list0 = cache.getRelsByName(name);
			if (StringUtils.isEmpty(srcOC) && StringUtils.isEmpty(tarOC)) {
				list = list0;
			} else {
				for (Relation rel : list0) {
					if ((StringUtils.isEmpty(srcOC) || srcOC.equals(rel
							.getSourceObjectClass()))
							&& (StringUtils.isEmpty(tarOC) || tarOC.equals(rel
									.getTargetObjectClass()))) {
						list.add(rel);
					}
				}
			}
		}

		return list;
	}

	/**
     * Returns the Entities that are the sources of the target relations of the given Entity.
     * 
     * Filters relations by relationName and tarObjClass.
     * 
	 * @param tar
	 * @param relationName
	 * @param srcObjClass
	 * @param maxResult
	 * @return
	 */
	public List<Entity> getSourcesForTargetRelation(Entity tar,
			String relationName, String srcObjClass, int maxResult) {
		return getSourcesForTargetRelation(tar.getId(), relationName,
				srcObjClass, maxResult);
	}

	/**
     * Returns the Entities that are the sources of the target relations of the Entity with the given tarId.
     * 
     * Filters relations by relationName and tarObjClass.
     * 
	 * @param tarId
	 * @param relationName
	 * @param srcObjClass
	 * @param maxResult
	 * @return
	 */
	public List<Entity> getSourcesForTargetRelation(Long tarId,
			String relationName, String srcObjClass, int maxResult) {
		List<Entity> rs = new ArrayList<Entity>();

		List<Relation> tarRelList = this.cache.getRelsByTarId(tarId);

		//long start = System.currentTimeMillis();
		int count = 0;
		for (Relation rel : tarRelList) {
			if (stringEquals(relationName, rel.getOwnValue())
					&& stringEquals(srcObjClass, rel.getSourceObjectClass())) {
				Entity ent = getEntityByIdReadOnly(rel.getSourceId());
				if (ent == null) {
                    logger.error("Relation source id does not exist! id="+rel.getSourceId()+" relation: "+rel);
                } else {
					rs.add(ent);
					count++;
					if (maxResult > 0 && count == maxResult) {
						break;
					}
				}
			}
		}
		//logger.debug("getSourcesForTargetRelation (loading sources) - execution time[ms]: "+(System.currentTimeMillis() - start));
		Collections.sort(rs, new EntitySortByNormalizedOwnValue());
		return rs;
	}

	/**
	 * Returns the Entities that are the targets of the source relations of the given Entity.
	 * 
	 * Filters relations by relationName and tarObjClass.
	 * 
	 * @param src
	 * @param relationName
	 * @param tarObjClass
	 * @param maxResult
	 * @return
	 */
	public List<Entity> getTargetsForSourceRelation(Entity src,
			String relationName, String tarObjClass, int maxResult) {
		return getTargetsForSourceRelation(src.getId(), relationName,
				tarObjClass, maxResult);
	}

	/**
	 * Returns the Entities that are the targets of the source relations of the Entity with the given srcId.
	 * 
	 * Filters relations by relationName and tarObjClass.
	 * 
	 * @param srcId
	 * @param relationName
	 * @param tarObjClass
	 * @param maxResult
	 * @return
	 */
	public List<Entity> getTargetsForSourceRelation(Long srcId,
			String relationName, String tarObjClass, int maxResult) {
		List<Entity> rs = new ArrayList<Entity>();

		try {
			Collection<Relation> srcRelList = this.cache.getRelsBySrcId(srcId);

			int count = 0;
			for (Relation rel : srcRelList) {
				if (stringEquals(relationName, rel.getOwnValue())
						&& stringEquals(tarObjClass, rel.getTargetObjectClass())) {
					Entity ent = getEntityByIdReadOnly(rel.getTargetId());
                    if (ent == null) {
                        logger.error("Relation target id does not exist! id="+rel.getTargetId()+" relation: "+rel);
                    } else {
                        rs.add(ent);
                        count++;
                        if (maxResult > 0 && count == maxResult) {
                            break;
                        }
                    }
				}
			}
			Collections.sort(rs, new EntitySortByNormalizedOwnValue());
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}

		return rs;
	}

	public List<Relation> getTargetRelations(Entity target,
			String relationName, String srcObjClass, int maxResult) {
		//long start = System.currentTimeMillis();
		List<Relation> list = new ArrayList<Relation>();

		List<Relation> tarRelList = this.cache.getRelsByTarId(target.getId());

		int count = 0;
		for (Relation rel : tarRelList) {
			if (stringEquals(relationName, rel.getOwnValue())
					&& stringEquals(srcObjClass, rel.getSourceObjectClass())) {
				Entity source = getEntityById(rel.getSourceId());
				if (source != null) {
					rel.setTarget(target);
					rel.setSource(source);
					list.add(rel);
					count++;
					if (maxResult > 0 && count == maxResult) {
						break;
					}
				} else {
					logger.error("Relation without source: "+ rel.toString());
				}

			}
		}
		Collections.sort(list, new RelationSortBySourceOW());
		//logger.debug("getTargetRelations - execution time[ms]: "+ (System.currentTimeMillis() - start));
		return list;
	}

	/**
	 * Returns a list of relations found by entity source.
	 * 
	 * @param source
	 * @param relationName
	 * @param tarObjClass
	 * @param maxResult
	 * @return
	 */
	public List<Relation> getSourceRelations(Entity source,
			String relationName, String tarObjClass, int maxResult)
			throws Exception {

		//long start = System.currentTimeMillis();
		List<Relation> list = new ArrayList<Relation>();

		// the collection coll should not be modified
		// otherwise java.util.ConcurrentModificationException will be thrown.
		// be carefully using getEntityContent, cause it changes the cache

		List<Relation> srcRelList = this.cache.getRelsBySrcId(source.getId());

		int count = 0;
		for (Relation rel : srcRelList) {
			if (stringEquals(relationName, rel.getOwnValue())
					&& stringEquals(tarObjClass, rel.getTargetObjectClass())) {
				rel.setSource(source);
				rel.setTarget(getEntityById(rel.getTargetId()));
				list.add(rel);
				count++;
				if (maxResult > 0 && count == maxResult) {
					break;
				}
			}
		}
		Collections.sort(list, new RelationSortByTargetOW());
		//logger.debug("getSourceRelations - execution time[ms]: "+ (System.currentTimeMillis() - start));

		return list;
	}

	/**
	 * Load and attach the Attributes and Relations of the given Entity.
	 * 
	 * Tries to loads from cache first and then from DB, saving to cache.
	 * 
	 * Returns a cloned Entity.
	 * 
	 * This method should not be used inside this Wrapper class, because it
	 * could throw a java.util.ConcurrentModificationException.
	 * 
	 * @param ent
	 * @return
	 */
	public Entity getEntityContent(Entity ent) {
		try {
			if (ent.isLightweight()) {
				Entity e = this.cache.getEntityContent(ent);
				if (e == null) {
					e = this.getPS().getEntityContent(ent);
					this.cache.saveEntity(e);
				} else {
					ent = e;
				}
			}
			return (Entity) ent.clone();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}

	public Entity getEntityContentReadOnly(Entity ent) throws Exception {
		if (ent.isLightweight()) {
			Entity e = this.cache.getEntityContent(ent);
			if (e == null) {
				e = this.getPS().getEntityContent(ent);
				this.cache.saveEntity(e);
			} else {
				ent = e;
			}
		}
		return ent;
	}

	/**
	 * Return an entity with the given id from the cache.
	 * 
	 * The entity returned could be LW or not, it depends on the cache.
	 * additionally, it will be always a clone of the version in cache,
	 * therefore it method is thought for editing's issues.
	 * 
	 * @param id
	 * @return
	 */
	public Entity getEntityById(Long id) {
		Entity ent = this.cache.getEntityById(id);
		return ent;
	}

	/**
	 * Return an entity with the given id from the cache.
	 * 
	 * Do not modify the entity!
	 * 
	 * @param id
	 * @return
	 */
	public Entity getEntityByIdReadOnly(Long id) {
		Entity ent = this.cache.getEntityByIdReadOnly(id);
		return ent;
	}

	public Entity getClonedEntityById(Long id) {
		return (Entity) getEntityById(id).clone();
	}

	/**
	 * This method should not be used inside this Wrapper class, because it
	 * could throws an java.util.ConcurrentModificationException.
	 * 
	 * @param id
	 * @return
	 */
	public Entity getEntityByIdWithContent(Long id) {
		try {
			Entity ent = getEntityById(id);
			if (ent != null && ent.isLightweight()) {
				ent = getEntityContent(ent);
			}
			return ent;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}

	public Entity getEntityByIdWithContentReadOnly(Long id) throws Exception {
		Entity ent = getEntityByIdReadOnly(id);
		if (ent.isLightweight()) {
			ent = getEntityContentReadOnly(ent);
		}
		return ent;
	}

	/**
	 * Returns if ow equals term if term is not empty. Returns true if term is empty.
	 * 
	 * @param term
	 * @param ow
	 * @return
	 */
	public static boolean stringEquals(String term, String ow) {
		if (StringUtils.isEmpty(term))
			return true;
		return term.equals(ow);
	}

	// ////****
	public Entity getLightweightEntityById(Long id) {
		Entity entity = null;
		if (id != null) {
			List<Entity> list = this.getPS().getLightweightEntities(
					Node.SYS_STATUS_CURRENT_VERSION, id, null, null, null,
					true, -1);
			if (list.size() > 0) {
				entity = list.get(0);
			}
		}
		return entity;
	}

	public List<Entity> getLightweightAssertions(String objectClass,
			String ownValue, int maxResult) {
		return getPS().getLightweightEntities(Node.SYS_STATUS_CURRENT_VERSION,
				null, Node.TYPE_ABOX, objectClass, ownValue, true, maxResult);
	}

	/**
	 * Save the entity. Calls {@link #saveEntity(String, String, EditIntent)}.
	 * 
	 * @param entity
	 * @param user
	 * @throws Exception
	 */
	public void saveAssertion(Entity entity, String user) throws Exception {
		this.saveEntity(entity, user, null);
	}

	/**
	 * <p>
	 * This method saves a non-persistent entity.
	 * </p>
	 * <p>
	 * The reason of this is that nodes are stored only in the DB and not in the
	 * cache.
	 * </p>
	 * <p>
	 * Later, when the nodes are required, they will be loaded from the BD to
	 * the cache automatically. However there are some structures like
	 * attributes by entId that become inconsistent using this method.
	 * </p>
	 * 
	 * @param node
	 *            non-persistent node.
	 * @param user
	 * @throws Exception
	 */
	public void saveNodeOnlyForScripts(Node node, String user) throws Exception {
		if (node.isPersistent()) {
			throw new Exception(
					"This method can save only non-persistent nodes. Because the nodes are stored only in the DB in not in the Cache.");
		}
		if (StringUtils.isEmpty(node.getType())) {
			throw new Exception("The type of the node can not be empty");
		}
		node.setUser(user);
		this.getPS().saveNode(node);

		// if(StringUtils.isNotEmpty(node.getType())){
		// if(node.getType().equals(Node.TYPE_ABOX)){
		//
		// }else if(node.getType().equals(Node.TYPE_TBOX)){
		//
		// }
		// }
	}

	public void removeNode(Node node) {
		this.getPS().removeNode(node);
	}

	public Map<Long, Long> saveEntityListAsNew(List<Entity> entities, String user,
			boolean testWrongRelations) throws Exception {
		logger.info("\n ### Making persistent Entities size total "
				+ entities.size() + " ### \n");
		int sizePart = 1000;
		List<Entity> tmpList = null;

		Map<Long, Long> idMap = new HashMap<Long, Long>();

		for (int j = 0; j <= (entities.size() / sizePart); j++) {
			tmpList = new ArrayList<Entity>(sizePart);
			for (int i = (j * sizePart); i < ((j + 1) * sizePart); i++) {

				if (i < entities.size()) {
					if (StringUtils.isNotEmpty(user)) {
						entities.get(i).setUser(user);
					}

					if (testWrongRelations
							&& hasWrongRelations(entities.get(i))) {
						throw new Exception("Wrong relations "
								+ entities.get(i));
					}

					tmpList.add(entities.get(i));
				} else {
					break;
				}
			}

			logger.info("\nMaking persistent Entities part " + (j + 1) + " of "
					+ ((entities.size() / sizePart) + 1) + " size="
					+ tmpList.size());
			idMap = this.getPS().saveEntityListAsNew(tmpList, idMap);
		}
		return idMap;
	}
	
	/*
	public void saveEntityList(List<Entity> entities, String user,
			boolean testWrongRelations) throws Exception {
		logger.info("\n ### Making persistent Entities size total "
				+ entities.size() + " ### \n");
		int sizePart = 1000;
		List<Entity> tmpList = null;

		for (int j = 0; j <= (entities.size() / sizePart); j++) {
			tmpList = new ArrayList<Entity>(sizePart);
			for (int i = (j * sizePart); i < ((j + 1) * sizePart); i++) {

				if (i < entities.size()) {
					if (StringUtils.isNotEmpty(user)) {
						entities.get(i).setUser(user);
					}

					if (testWrongRelations
							&& hasWrongRelations(entities.get(i))) {
						throw new Exception("Wrong relations "
								+ entities.get(i));
					}

					tmpList.add(entities.get(i));
				} else {
					break;
				}
			}

			logger.info("\nMaking persistent Entities part " + (j + 1) + " of "
					+ ((entities.size() / sizePart) + 1) + " size="
					+ tmpList.size());
			this.getPS().saveEntityList(tmpList);
			
			for(Entity ent : tmpList){
				logger.info("[U=" + user + "] SaveEntity " + ent.toSmallString());
			}
			
		}
	}*/

	public void saveConcept(Entity entity) throws Exception {
		entity.setType(Node.TYPE_TBOX);
		entity.setObjectClass(Node.TYPE_TBOX);
		this.getPS().saveEntity(entity);
	}

	public void saveNodeListOnlyForScripts(List<Node> nodeList, String user)
			throws Exception {
		logger.debug("### Making persistent Nodes size total "
				+ nodeList.size() + " ###");
		List<Node> list = null;
		int sizePart = 1000;
		for (int j = 0; j <= (nodeList.size() / sizePart); j++) {
			list = new ArrayList<Node>();
			for (int i = (j * sizePart); i < ((j + 1) * sizePart); i++) {
				if (i < nodeList.size()) {
					nodeList.get(i).setUser(user);
					list.add(nodeList.get(i));
				}
			}
			logger.debug("Making persistent Nodes part " + (j + 1) + " of "
					+ ((nodeList.size() / sizePart) + 1) + " size="
					+ list.size());
			this.getPS().saveNodeList(list);
		}
	}

	public void saveNodeListOnlyForScripts(List<Node> nodeList)
			throws Exception {
		logger.info("\n ### Making persistent Nodes size total "
				+ nodeList.size() + " ### \n");
		int sizePart = 1000;
		List<Node> list = null;
		for (int j = 0; j <= (nodeList.size() / sizePart); j++) {
			list = new ArrayList<Node>();
			for (int i = (j * sizePart); i < ((j + 1) * sizePart); i++) {
				if (i < nodeList.size()) {
					list.add(nodeList.get(i));
				}
			}
			logger.info("Making persistent Nodes part " + (j + 1) + " of "
					+ ((nodeList.size() / sizePart) + 1) + " size="
					+ list.size());
			this.getPS().saveNodeList(list);
		}
		// this.persistenceService.saveNodeList(nodeList);
	}

	public List<Entity> getConcepts() {
		return this.getPS().getEntities(null, Node.SYS_STATUS_CURRENT_VERSION,
				Node.TYPE_TBOX, null);
	}

	public List<Entity> getAssertion() {
		return this.getPS().getEntities(null, Node.SYS_STATUS_CURRENT_VERSION,
				Node.TYPE_ABOX, null);
	}

	public List<Entity> getLightweightAssertionsByExactOwnValue(
			String objectClass, String ownValue, int maxResult) {
		return getPS().getLightweightEntities(Node.SYS_STATUS_CURRENT_VERSION,
				null, Node.TYPE_ABOX, objectClass, ownValue, false, maxResult);
	}

	public void removeNodeList(List<Node> nodeList) {
		//System.out.println("\n ### Deleting Nodes size total "+ nodeList.size() + " ### \n");
		this.getPS().removeNodeList(nodeList);
	}

	public void deleteAllConcepts() {
		// persistenceService.deleteEntities(null, Node.TYPE_TBOX, true);
		getPS().dropDefinitions();
	}

	public void importOM3Concepts(String fileName, Boolean dropConcepts) {
		getPS().setImportModus(true);
		ImportOM3Util.importConcepts(this, fileName, dropConcepts);
		getPS().setImportModus(false);
	}

	// ///

	public PersistenceService getPS() {
		return this.cache.getPs();
	}

	public CacheService getCache() {
		return cache;
	}

	public void setCache(CacheService cache) {
		this.cache = cache;
	}
	
	public List<ViewerAttribute> getViewerAttributes(Long page){
		List<ViewerAttribute> list = cache.getViewerAttributes(page);
		Collections.sort(list);
		return list;
	}
	
	public List<ViewerAttribute> getViewerAttributes4Edition(Long page) throws CloneNotSupportedException{
		List<ViewerAttribute> list = new ArrayList<ViewerAttribute>();
		for(ViewerAttribute att : cache.getViewerAttributes(page)){
			list.add((ViewerAttribute)att.clone());
		}
		Collections.sort(list);
		return list;
	}
	
	public Collection<ViewerPage> getViewerPages(){
		return cache.getViewerPages();
	}
	
	public ViewerPage getViewerPage(Long id){
		return cache.getViewerPageMap().get(id);
	}
	
	public ViewerPage getViewerPage(String definition){
		for(ViewerPage page : cache.getViewerPages()){
			if(page.getDefinition().equals(definition)){
				return page;
			}			
		}
		return null;
	}
	
	public ViewerPage getViewerPage4Edition(Long id) throws CloneNotSupportedException{
		ViewerPage page = cache.getViewerPageMap().get(id);
		return (page != null) ? (ViewerPage) page.clone() : null; 
	}
	
	public ViewerPage saveViewerPage(ViewerPage page, String user){
		page.setUser(user);
		return cache.saveViewerPage(page);
		
	}
	
	public ViewerAttribute saveViewerAttribute(ViewerPage page, ViewerAttribute att, String user) throws Exception{
		att.setUser(user);
		return cache.saveViewerAttribute(page, att);
	}
	
	/**
	 * Removes a page and all attributes associated with it.
	 * @param page
	 * @return
	 */
	public int removeViewerPage(ViewerPage page){
		
		return (page.getId() != null) ? cache.removeViewerPage(page.getId()) : 0;
	}
	
	public boolean removeViewerAnttribute(ViewerAttribute att){
		return (att.getId() != null) ? cache.removeAtt(att.getId()) : false;
	}
}