private static Set<ExtTrustRelationship> entitiesToExtRelationships(
      final Set<ITrustedEntity> entities, final TrustQuery query) {

    final Set<ExtTrustRelationship> result =
        new LinkedHashSet<ExtTrustRelationship>(entities.size());

    final TrustValueType trustValueType = query.getTrustValueType();
    for (final ITrustedEntity entity : entities) {

      final Set<TrustEvidence> evidenceSet = iEvidenceToEvidence(entity.getEvidence());
      // TODO Needs optimisation
      final Set<TrustEvidence> directEvidenceSet = new LinkedHashSet<TrustEvidence>();
      final Set<TrustEvidence> indirectEvidenceSet = new LinkedHashSet<TrustEvidence>();
      for (final TrustEvidence evidence : evidenceSet) {
        if (evidence.getSourceId() == null) {
          directEvidenceSet.add(evidence);
        } else {
          indirectEvidenceSet.add(evidence);
        }
      }

      if (entity.getDirectTrust().getValue() != null
          && (null == trustValueType || TrustValueType.DIRECT == trustValueType)) {
        result.add(
            new ExtTrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.DIRECT,
                entity.getDirectTrust().getValue(),
                entity.getDirectTrust().getLastUpdated(),
                directEvidenceSet));
      }
      if (entity.getIndirectTrust().getValue() != null
          && (null == trustValueType || TrustValueType.INDIRECT == trustValueType)) {
        result.add(
            new ExtTrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.INDIRECT,
                entity.getIndirectTrust().getValue(),
                entity.getIndirectTrust().getLastUpdated(),
                indirectEvidenceSet));
      }
      if (entity.getUserPerceivedTrust().getValue() != null
          && (null == trustValueType || TrustValueType.USER_PERCEIVED == trustValueType)) {
        result.add(
            new ExtTrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.USER_PERCEIVED,
                entity.getUserPerceivedTrust().getValue(),
                entity.getUserPerceivedTrust().getLastUpdated(),
                new HashSet<TrustEvidence>()));
      }
    }

    return result;
  }
  private static Set<TrustRelationship> entitiesToRelationships(
      final Set<ITrustedEntity> entities, final TrustQuery query) {

    final Set<TrustRelationship> result = new LinkedHashSet<TrustRelationship>();

    final TrustValueType trustValueType = query.getTrustValueType();
    for (final ITrustedEntity entity : entities) {

      if (entity.getDirectTrust().getValue() != null
          && (null == trustValueType || TrustValueType.DIRECT == trustValueType)) {
        result.add(
            new TrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.DIRECT,
                entity.getDirectTrust().getValue(),
                entity.getDirectTrust().getLastUpdated()));
      }
      if (entity.getIndirectTrust().getValue() != null
          && (null == trustValueType || TrustValueType.INDIRECT == trustValueType)) {
        result.add(
            new TrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.INDIRECT,
                entity.getIndirectTrust().getValue(),
                entity.getIndirectTrust().getLastUpdated()));
      }
      if (entity.getUserPerceivedTrust().getValue() != null
          && (null == trustValueType || TrustValueType.USER_PERCEIVED == trustValueType)) {
        result.add(
            new TrustRelationship(
                entity.getTrustorId(),
                entity.getTrusteeId(),
                TrustValueType.USER_PERCEIVED,
                entity.getUserPerceivedTrust().getValue(),
                entity.getUserPerceivedTrust().getLastUpdated()));
      }
    }

    return result;
  }
  private Double retrieveLocalTrustValue(final Requestor requestor, final TrustQuery query)
      throws TrustException {

    final Set<ITrustedEntity> entities = this.retrieveTrustedEntities(query);

    if (entities.isEmpty()) {
      return null;
    } else if (entities.size() == 1) {
      final ITrustedEntity entity = entities.iterator().next();
      if (TrustValueType.DIRECT == query.getTrustValueType()) {
        return entity.getDirectTrust().getValue();
      } else if (TrustValueType.INDIRECT == query.getTrustValueType()) {
        return entity.getIndirectTrust().getValue();
      } else { // if (TrustValueType.USER_PERCEIVED == query.getTrustValueType())
        return entity.getUserPerceivedTrust().getValue();
      }
    } else {
      throw new NonUniqueTrustQueryResultException(
          "Query returned " + entities.size() + " results");
    }
  }
  /*
   * @see org.societies.privacytrust.trust.api.engine.IIndirectTrustEngine#evaluate(org.societies.api.privacytrust.trust.model.TrustedEntityId, org.societies.privacytrust.trust.api.evidence.model.ITrustEvidence)
   */
  @Override
  public Set<ITrustedEntity> evaluate(
      final TrustedEntityId trustorId, final ITrustEvidence evidence) throws TrustEngineException {

    if (trustorId == null) {
      throw new NullPointerException("trustorId can't be null");
    }
    if (evidence == null) {
      throw new NullPointerException("evidence can't be null");
    }

    LOG.debug("evaluate: trustorId={}, evidence={}", trustorId, evidence);

    final Set<ITrustedEntity> resultSet = new HashSet<ITrustedEntity>();

    if (!this.areRelevant(trustorId, evidence)) {
      return resultSet;
    }

    try {
      // Does similarity between trustor and subject needs re-evaluation?
      boolean doSimilarityEval = false;
      // Create the trusted entity the evidence object refers to if not already available
      ITrustedEntity trustee =
          (ITrustedEntity) this.trustRepo.retrieveEntity(trustorId, evidence.getObjectId());
      if (trustee == null) {
        trustee = super.trustRepo.createEntity(trustorId, evidence.getObjectId());
      } else {
        doSimilarityEval = true;
      }
      LOG.debug("evaluate: doSimilarity={}", doSimilarityEval);
      resultSet.add(trustee);

      switch (evidence.getType()) {

          // Update value
        case DIRECTLY_TRUSTED:
          // Check if similarity between trustor and subject needs re-evaluation
          if (doSimilarityEval
              && TrustedEntityType.CSS == evidence.getSubjectId().getEntityType()) {
            final ITrustedCss subject =
                (ITrustedCss) super.createEntityIfAbsent(trustorId, evidence.getSubjectId());
            final Double similarity =
                this.trustSimilarityEvaluator.evaluateCosineSimilarity(
                    trustorId, evidence.getSubjectId());
            LOG.debug("evaluate: similarity={}", similarity);
            if (similarity != null && !Double.isNaN(similarity)) {
              subject.setSimilarity(similarity);
              super.trustRepo.updateEntity(subject);
            }
          }
          // Fetch top N users
          final Map<TrustedEntityId, ITrustedCss> topNCssMap = this.retrieveTopNCss(trustorId);
          LOG.debug("evaluate: topNCssMap={}", topNCssMap);
          double weightedOpinionSum = 0d;
          double weightSum = 0d;
          // Retrieve all Indirect Trust Evidence related to the object
          // referenced in the specified TrustEvidence
          final Set<ITrustEvidence> evidenceSet =
              super.trustEvidenceRepo.retrieveLatestEvidence(
                  null, evidence.getObjectId(), evidence.getType(), null);
          LOG.debug("evaluate: evidenceSet={}", evidenceSet);
          for (final ITrustEvidence relatedEvidence : evidenceSet) {
            if (!(relatedEvidence.getInfo() instanceof Double)) {
              LOG.warn("Related evidence " + relatedEvidence + " has no trust value!");
              continue;
            }
            final ITrustedCss opinionSource = topNCssMap.get(relatedEvidence.getSubjectId());
            if (opinionSource == null) {
              LOG.warn(
                  "Could not find CSS trust relationship with related evidence subject '"
                      + relatedEvidence.getSubjectId()
                      + "'");
              continue;
            }
            final Double weight = evaluateWeight(opinionSource);
            Double weightedOpinion = null;
            if (weight != null) {
              final double meanOpinion =
                  this.retrieveMeanTrustOpinion(relatedEvidence.getSubjectId());
              weightedOpinion = weight * ((Double) relatedEvidence.getInfo() - meanOpinion);
              LOG.debug(
                  "evaluate: subjectId={}, meanOpinion={}, weightedOpinion={}",
                  new Object[] {relatedEvidence.getSubjectId(), meanOpinion, weightedOpinion});
            }
            if (weightedOpinion == null) {
              LOG.warn(
                  "Ignoring related evidence " + relatedEvidence + ": Weighted opinion is null");
              continue;
            }
            weightedOpinionSum += weightedOpinion;
            weightSum += Math.abs(weight);
          }
          LOG.debug("evaluate: weightedOpinionSum={}, weightSum={}", weightedOpinionSum, weightSum);
          // t_x,i = avg(t_x) + weighted opinions
          double value =
              super.trustRepo.retrieveMeanTrustValue(trustorId, TrustValueType.DIRECT, null);
          final double confidence;
          if (weightSum > 0) {
            value += weightedOpinionSum / weightSum;
            confidence = 0.5d; // TODO constant or what?
          } else {
            confidence = 0.25d; // TODO constant or what?
          }
          LOG.debug("evaluate: value={}", value);
          if (value > 1) {
            value = 1.0d; // TODO use constant
          } else if (value < 0) {
            value = 0.0d; // TODO use constant
          }
          trustee.getIndirectTrust().setValue(value);
          trustee.getIndirectTrust().setConfidence(confidence);
          break;

        default:
          throw new TrustEngineException("Unsupported type: " + evidence.getType());
      }

      // Add related evidence to trustee
      trustee.addEvidence(evidence);

      // Persist updated TrustedEntities in the Trust Repository
      for (final ITrustedEntity entity : resultSet) {
        super.trustRepo.updateEntity(entity);
      }

    } catch (Exception e) {
      throw new TrustEngineException(
          "Could not evaluate indirect trust evidence " + evidence + ": " + e.getLocalizedMessage(),
          e);
    }

    return resultSet;
  }