/**
   * resolve attribute role.
   *
   * @param ctx EvaluationContext
   * @param attributeIdValue whole attribute
   * @param resolvableAttribute resolvable part of attribute
   * @param tail tail after resolvable part
   * @return Object[] result
   * @throws de.escidoc.core.common.exceptions.system.WebserverSystemException
   * @throws de.escidoc.core.common.exceptions.application.notfound.GrantNotFoundException
   * @throws de.escidoc.core.common.exceptions.application.notfound.ResourceNotFoundException
   */
  private Object[] resolveRoleAttribute(
      final EvaluationCtx ctx,
      final String attributeIdValue,
      final String resolvableAttribute,
      final String tail)
      throws GrantNotFoundException, WebserverSystemException, ResourceNotFoundException {
    final String userOrGroupId =
        FinderModuleHelper.retrieveSingleResourceAttribute(ctx, Constants.URI_RESOURCE_ID, true);
    final String grantId =
        FinderModuleHelper.retrieveSingleResourceAttribute(ctx, Constants.URI_SUBRESOURCE_ID, true);
    final String roleId;
    if (grantId == null || grantId.length() == 0) {
      // if no grantId is present
      // fetch grant-attribute from invocation-mapping
      roleId = fetchSingleResourceAttribute(ctx, resolvableAttribute + "-new");
    } else {
      final RoleGrant grant =
          resolvableAttribute.matches(".*" + XmlUtility.NAME_USER_ACCOUNT + ".*")
              ? getUserAccountGrant(ctx, userOrGroupId, grantId)
              : getUserGroupGrant(ctx, grantId);
      assertGrant(grantId, grant);
      roleId = grant.getRoleId();
    }

    final EvaluationResult result =
        CustomEvaluationResultBuilder.createSingleStringValueResult(roleId);
    return new Object[] {result, resolvableAttribute};
  }
  /**
   * resolve attribute created-by.
   *
   * @param ctx EvaluationContext
   * @param attributeIdValue whole attribute
   * @param resolvableAttribute resolvable part of attribute
   * @param tail tail after resolvable part
   * @return Object[] result
   * @throws de.escidoc.core.common.exceptions.system.WebserverSystemException
   * @throws de.escidoc.core.common.exceptions.system.SqlDatabaseSystemException
   * @throws de.escidoc.core.common.exceptions.application.notfound.GrantNotFoundException
   * @throws de.escidoc.core.common.exceptions.application.notfound.ResourceNotFoundException
   */
  private Object[] resolveCreatedByAttribute(
      final EvaluationCtx ctx,
      final String attributeIdValue,
      final String resolvableAttribute,
      final String tail)
      throws GrantNotFoundException, SqlDatabaseSystemException, ResourceNotFoundException,
          WebserverSystemException {
    final String userOrGroupId =
        FinderModuleHelper.retrieveSingleResourceAttribute(ctx, Constants.URI_RESOURCE_ID, true);
    final String grantId =
        FinderModuleHelper.retrieveSingleResourceAttribute(ctx, Constants.URI_SUBRESOURCE_ID, true);
    if (grantId == null || grantId.length() == 0) {
      throw new GrantNotFoundException("no grantId found");
    }
    final RoleGrant grant =
        resolvableAttribute.matches(".*" + XmlUtility.NAME_USER_ACCOUNT + ".*")
            ? userAccountDao.retrieveGrant(userOrGroupId, grantId)
            : userGroupDao.retrieveGrant(grantId);
    assertGrant(grantId, grant);
    final String createdBy = grant.getCreatorId();

    final EvaluationResult result =
        CustomEvaluationResultBuilder.createSingleStringValueResult(createdBy);
    return new Object[] {result, resolvableAttribute};
  }
  /**
   * resolve attribute assigned-on. check if tail is present and resolvable (dependent on variable
   * SUPPORTED_ASSIGNED_ON_OBJECT_ATTRIBUTES). if tail is not resolvable, mark whole attribute as
   * unresolvable.
   *
   * @param ctx EvaluationContext
   * @param attributeIdValue whole attribute
   * @param resolvableAttribute resolvable part of attribute
   * @param tail tail after resolvable part
   * @return Object[] result
   * @throws de.escidoc.core.common.exceptions.system.WebserverSystemException
   * @throws de.escidoc.core.common.exceptions.application.notfound.GrantNotFoundException
   * @throws de.escidoc.core.common.exceptions.system.TripleStoreSystemException
   * @throws de.escidoc.core.common.exceptions.application.notfound.ResourceNotFoundException
   * @throws de.escidoc.core.common.exceptions.system.SystemException
   */
  private Object[] resolveAssignedOnAttribute(
      final EvaluationCtx ctx,
      final String attributeIdValue,
      final String resolvableAttribute,
      final String tail)
      throws TripleStoreSystemException, SystemException, GrantNotFoundException,
          ResourceNotFoundException, WebserverSystemException {
    EvaluationResult result;
    final String userOrGroupId =
        FinderModuleHelper.retrieveSingleResourceAttribute(ctx, Constants.URI_RESOURCE_ID, true);
    final String grantId =
        FinderModuleHelper.retrieveSingleResourceAttribute(ctx, Constants.URI_SUBRESOURCE_ID, true);
    String assignedOnObjectId;
    if (grantId == null || grantId.length() == 0) {
      // if no grantId is present
      // fetch grant-attribute from invocation-mapping
      try {
        assignedOnObjectId = fetchSingleResourceAttribute(ctx, resolvableAttribute + "-new");
      } catch (final Exception e) {
        // not assigned to an object
        // so mark complete attribute as unresolvable
        result =
            CustomEvaluationResultBuilder.createSingleStringValueResult(
                de.escidoc.core.common.business.Constants.UNRESOLVED_ATTRIBUTE_VALUE);
        return new Object[] {result, attributeIdValue};
      }
    } else {
      final RoleGrant grant =
          resolvableAttribute.matches(".*" + XmlUtility.NAME_USER_ACCOUNT + ".*")
              ? getUserAccountGrant(ctx, userOrGroupId, grantId)
              : getUserGroupGrant(ctx, grantId);
      assertGrant(grantId, grant);
      assignedOnObjectId = grant.getObjectId();
    }
    if (assignedOnObjectId == null) {
      // not assigned on an object
      // so mark complete attribute as unresolvable
      result =
          CustomEvaluationResultBuilder.createSingleStringValueResult(
              de.escidoc.core.common.business.Constants.UNRESOLVED_ATTRIBUTE_VALUE);
      return new Object[] {result, attributeIdValue};
    }

    // check if tailing attribute is resolvable for assigned object-type
    if (tail != null) {
      final String objectType = fetchObjectType(ctx, assignedOnObjectId);
      if (objectType.equals(XmlUtility.NAME_COMPONENT) && tail.equals(XmlUtility.NAME_CONTEXT)) {
        // if we have to resolve the context of a component,
        // we first have to get the itemId and resolve context for
        // the itemId
        final List<String> itemIds =
            FinderModuleHelper.retrieveFromTripleStore(
                true,
                tsu.getRetrieveWhereClause(
                    true, TripleStoreUtility.PROP_COMPONENT, assignedOnObjectId, null, null, null),
                assignedOnObjectId,
                TripleStoreUtility.PROP_COMPONENT,
                this.tsu);
        if (itemIds == null || itemIds.isEmpty() || itemIds.size() != 1) {
          result =
              CustomEvaluationResultBuilder.createResourceNotFoundResult(
                  new ItemNotFoundException(
                      "item for component " + assignedOnObjectId + " not found"));
        } else {
          assignedOnObjectId = itemIds.get(0);
        }
      }
    }
    result = CustomEvaluationResultBuilder.createSingleStringValueResult(assignedOnObjectId);
    return new Object[] {result, resolvableAttribute};
  }
  /** 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};
  }