/**
   * Retrieves the handles of the user.
   *
   * @param ctx The evaluation context, which will be used as key for the cache.
   * @param userAccountId The id of the user account to get the handles for.
   * @param attributeIdValue The value of the attribute id that is currently resolved.
   * @return Returns the eSciDoc user handle of the subject (current user).
   * @throws WebserverSystemException Thrown in case of an internal error.
   * @throws UserAccountNotFoundException Thrown if no user account with provided id is found.
   */
  private Set retrieveUserHandle(
      final EvaluationCtx ctx, final String userAccountId, final String attributeIdValue)
      throws WebserverSystemException, UserAccountNotFoundException {

    Set<AttributeValue> result =
        (Set<AttributeValue>) getFromCache(XmlUtility.NAME_HANDLE, null, null, userAccountId, ctx);
    if (result == null) {
      final List userHandles;
      try {
        userHandles = getUserAccountDao().retrieveUserLoginDataByUserId(userAccountId);
        if (userHandles == null || userHandles.isEmpty()) {
          assertUserAccount(
              userAccountId, getUserAccountDao().retrieveUserAccountById(userAccountId));
        }
      } catch (final UserAccountNotFoundException e) {
        throw e;
      } catch (final Exception e) {
        final String errorMsg =
            StringUtility.format("Retrieving of attribute failed", attributeIdValue);
        throw new WebserverSystemException(errorMsg, e);
      }
      result = new HashSet<AttributeValue>();
      if (userHandles != null && !userHandles.isEmpty()) {
        for (final Object userHandle : userHandles) {
          final UserLoginData userLoginData = (UserLoginData) userHandle;
          result.add(new StringAttribute(userLoginData.getHandle()));
        }
      }
      putInCache(XmlUtility.NAME_HANDLE, null, null, userAccountId, ctx, result);
    }
    return result;
  }
  /**
   * Asserts that the grant is provided, i.e. it is not <code>null</code>.
   *
   * @param grantId The grant id for which the grant should be provided (should exist).
   * @param roleGrant The role grant to assert.
   * @throws GrantNotFoundException Thrown if assertion fails.
   */
  private static void assertGrant(final String grantId, final RoleGrant roleGrant)
      throws GrantNotFoundException {

    if (roleGrant == null) {
      throw new GrantNotFoundException(
          StringUtility.format("Grant with provided id does not exist", grantId));
    }
  }
  /**
   * Asserts that the user account is provided, i.e. it is not {@code null}.
   *
   * @param userId The user id for which the account should be provided (should exist).
   * @param userAccount The user account to assert.
   * @throws UserAccountNotFoundException Thrown if assertion fails.
   */
  private static void assertUserAccount(final String userId, final UserAccount userAccount)
      throws UserAccountNotFoundException {

    if (userAccount == null) {
      throw new UserAccountNotFoundException(
          StringUtility.format("User with provided id does not exist", userId));
    }
  }
  /**
   * Retrieve user-group grant from the system.
   *
   * @param ctx The evaluation context, which will be used as key for the cache.
   * @param grantId The grant id.
   * @return Returns the {@code RoleGrant} identified by the provided id.
   * @throws WebserverSystemException Thrown in case of an internal error.
   * @throws GrantNotFoundException Thrown if no grant with provided id exists.
   */
  private RoleGrant getUserGroupGrant(final EvaluationCtx ctx, final String grantId)
      throws WebserverSystemException, GrantNotFoundException {
    RoleGrant grant = (RoleGrant) getFromCache(XmlUtility.NAME_ID, null, null, grantId, ctx);
    if (grant == null) {
      try {
        grant = userGroupDao.retrieveGrant(grantId);
      } catch (final Exception e) {
        throw new WebserverSystemException(
            StringUtility.format("Exception during retrieval of the grant", e.getMessage()), e);
      }
    }
    assertGrant(grantId, grant);

    putInCache(XmlUtility.NAME_ID, null, null, grantId, ctx, grant);
    return grant;
  }
  /**
   * Retrieve user-group grant from the system.
   *
   * @param ctx The evaluation context, which will be used as key for the cache.
   * @param grantId The grant id.
   * @return Returns the <code>RoleGrant</code> identified by the provided id.
   * @throws WebserverSystemException Thrown in case of an internal error.
   * @throws GrantNotFoundException Thrown if no grant with provided id exists.
   */
  private RoleGrant getUserGroupGrant(final EvaluationCtx ctx, final String grantId)
      throws WebserverSystemException, GrantNotFoundException {
    final StringBuffer key = StringUtility.concatenateWithColon(XmlUtility.NAME_ID, grantId);
    RoleGrant grant = (RoleGrant) RequestAttributesCache.get(ctx, key.toString());
    if (grant == null) {
      try {
        grant = userGroupDao.retrieveGrant(grantId);
      } catch (final Exception e) {
        throw new WebserverSystemException(
            StringUtility.format("Exception during retrieval of the grant", e.getMessage()), e);
      }
    }
    assertGrant(grantId, grant);

    RequestAttributesCache.put(ctx, key.toString(), grant);
    return grant;
  }
  /**
   * Retrieve User Account from the system.
   *
   * @param ctx The evaluation context, which will be used as key for the cache.
   * @param userAccountId The user account id.
   * @return Returns the {@code UserAccount} identified by the provided id.
   * @throws WebserverSystemException Thrown in case of an internal error.
   * @throws UserAccountNotFoundException Thrown if no user account with provided id exists.
   */
  private UserAccount retrieveUserAccount(final EvaluationCtx ctx, final String userAccountId)
      throws WebserverSystemException, UserAccountNotFoundException {

    UserAccount userAccount =
        (UserAccount) getFromCache(XmlUtility.NAME_ID, null, null, userAccountId, ctx);
    if (userAccount == null) {
      try {
        userAccount = getUserAccountDao().retrieveUserAccount(userAccountId);
      } catch (final Exception e) {
        throw new WebserverSystemException(
            StringUtility.format("Exception during retrieval of the user account", e.getMessage()),
            e);
      }
    }

    assertUserAccount(userAccountId, userAccount);

    putInCache(XmlUtility.NAME_ID, null, null, userAccountId, ctx, userAccount);
    return userAccount;
  }
  /** See Interface for functional description. */
  @Override
  protected Object[] resolveLocalPart(
      final String attributeIdValue,
      final EvaluationCtx ctx,
      final String resourceId,
      final String resourceObjid,
      final String resourceVersionNumber)
      throws EscidocException {

    // determine the id of the user account and to simplify the further
    // work, replace INTERNAL_SUBJECT_ATTRIBUTE_PREFIX by
    // INTERNAL_RESOURCE_USER_ACCOUNT_ATTRIBUTE_PREFIX in case of an
    // subject attribute
    final String internalAttributeIdValue;
    final String userAccountId;
    boolean isSubjectAttribute = false;
    if (PATTERN_IS_SUBJECT_ATTRIBUTE_ID.matcher(attributeIdValue).find()) {
      isSubjectAttribute = true;
      userAccountId =
          FinderModuleHelper.retrieveSingleSubjectAttribute(ctx, Constants.URI_SUBJECT_ID, true);
      if (userAccountId == null) {
        final StringBuilder errorMsg =
            new StringBuilder("The subject (user) of the request cannot be ");
        errorMsg.append("identified, the ");
        errorMsg.append(Constants.URI_SUBJECT_ID);
        errorMsg.append(" may not have been set.");
        throw new WebserverSystemException(errorMsg.toString());
      }
      final Matcher matcher = PATTERN_SUBJECT_ATTRIBUTE_PREFIX.matcher(attributeIdValue);
      internalAttributeIdValue = matcher.replaceFirst(AttributeIds.USER_ACCOUNT_ATTR_PREFIX);
    } else {
      userAccountId = FinderModuleHelper.getResourceId(ctx);
      if (FinderModuleHelper.isNewResourceId(userAccountId)) {
        return null;
      }
      internalAttributeIdValue = attributeIdValue;
    }
    // ask cache for previously cached results
    EvaluationResult result =
        (EvaluationResult)
            getFromCache(
                resourceId, resourceObjid, resourceVersionNumber, internalAttributeIdValue, ctx);

    String resolvedAttributeIdValue = null;
    if (result == null) {
      // check if attributes of an anonymous user shall be retrieved
      if (UserContext.isIdOfAnonymousUser(userAccountId)) {
        // The anonymous user does not have an account, for each
        // attribute the value of the anonymous identifier is returned.
        result =
            CustomEvaluationResultBuilder.createSingleStringValueResult(
                UserContext.ANONYMOUS_IDENTIFIER);
        // the resolved id is set to the complete id, as no further
        // resolving is possible.
        resolvedAttributeIdValue = internalAttributeIdValue;
      } else {
        if (ATTR_USER_HANDLE.equals(internalAttributeIdValue)) {
          final Set userHandles = retrieveUserHandle(ctx, userAccountId, internalAttributeIdValue);
          result =
              new EvaluationResult(new BagAttribute(Constants.URI_XMLSCHEMA_STRING, userHandles));
          resolvedAttributeIdValue = ATTR_USER_HANDLE;
        } else if (ATTR_USER_ROLE_SCOPE.matcher(internalAttributeIdValue).matches()) {
          result = fetchRoleScopes(userAccountId, internalAttributeIdValue);
          resolvedAttributeIdValue = internalAttributeIdValue;
        } else if (ATTR_USER_GROUP_MEMBERSHIP.equals(internalAttributeIdValue)) {
          result = fetchUserGroups(userAccountId);
          resolvedAttributeIdValue = internalAttributeIdValue;
        } else {
          // Fetch the user account and return the appropriate value
          final UserAccount userAccount;
          try {
            userAccount = retrieveUserAccount(ctx, userAccountId);
          } catch (final UserAccountNotFoundException e) {
            if (isSubjectAttribute) {
              throw new UserAccountNotFoundException(
                  StringUtility.format(
                      "Account of subject not found.", userAccountId, e.getMessage()),
                  e);
            } else {
              throw e;
            }
          }
          final Pattern p = PATTERN_PARSE_USER_ACCOUNT_ATTRIBUTE_ID;
          final Matcher idMatcher = p.matcher(internalAttributeIdValue);
          if (idMatcher.find()) {
            resolvedAttributeIdValue = idMatcher.group(1);
            if (userAccount != null) {
              if (ATTR_USER_ID.equals(resolvedAttributeIdValue)) {
                final String nextResourceId = userAccount.getId();
                result =
                    CustomEvaluationResultBuilder.createSingleStringValueResult(nextResourceId);
              } else if (ATTR_USER_LOGIN_NAME.equals(resolvedAttributeIdValue)) {
                result =
                    CustomEvaluationResultBuilder.createSingleStringValueResult(
                        userAccount.getLoginname());
              } else if (ATTR_USER_NAME.equals(resolvedAttributeIdValue)) {
                result =
                    CustomEvaluationResultBuilder.createSingleStringValueResult(
                        userAccount.getName());
              } else if (ATTR_CREATED_BY.equals(resolvedAttributeIdValue)) {
                result =
                    CustomEvaluationResultBuilder.createSingleStringValueResult(
                        userAccount.getUserAccountByCreatorId().getId());
              } else if (ATTR_MODIFIED_BY.equals(resolvedAttributeIdValue)) {
                result =
                    CustomEvaluationResultBuilder.createSingleStringValueResult(
                        userAccount.getUserAccountByModifiedById().getId());
              } else if (ATTR_USER_ORGANIZATIONAL_UNIT.equals(resolvedAttributeIdValue)) {
                result = fetchUserAccountOus(userAccount, false);
              } else if (ATTR_USER_ORGANIZATIONAL_UNIT_WITH_CHILDREN.equals(
                  resolvedAttributeIdValue)) {
                result = fetchUserAccountOus(userAccount, true);
              }
            }
          }
        }
      }
    }
    if (result == null) {
      return null;
    }
    if (isSubjectAttribute) {
      // revert previously Subject -> Resource change
      final Matcher matcher =
          PATTERN_USER_ACCOUNT_ATTRIBUTE_PREFIX.matcher(resolvedAttributeIdValue);
      resolvedAttributeIdValue = matcher.replaceFirst(AttributeIds.SUBJECT_ATTR_PREFIX);
    }
    putInCache(resourceId, resourceObjid, resourceVersionNumber, attributeIdValue, ctx, result);
    return new Object[] {result, resolvedAttributeIdValue};
  }