class AttributeDescriptor {

  Object readReflection(Object target, ExecutionContext executionContext)
      throws NameNotFoundException, QueryInvocationTargetException {
    Support.Assert(target != null);
    Support.Assert(target != QueryService.UNDEFINED);
    if (target instanceof Token) {
      return QueryService.UNDEFINED;
    }

    Class resolutionClass = target.getClass();
    Member m = getReadMember(resolutionClass);
    try {
      if (m instanceof Method) {
        try {
          Method method = (Method) m;
          MethodInvocationAuthorizer authorizer = executionContext.getMethodInvocationAuthorizer();

          // CQs are generally executed on individual events, so caching is just an overhead.
          if (executionContext.isCqQueryContext()) {
            if (!authorizer.authorize(method, target)) {
              throw new NotAuthorizedException(UNAUTHORIZED_STRING + method.getName());
            }
          } else {
            // Try to use previous result so authorizer gets invoked only once per query.
            boolean authorizationResult;
            Boolean cachedResult = (Boolean) executionContext.cacheGet(method);

            if (cachedResult == null) {
              // First time, evaluate and cache result.
              authorizationResult = authorizer.authorize(method, target);
              executionContext.cachePut(method, authorizationResult);
            } else {
              // Use cached result.
              authorizationResult = cachedResult;
            }

            if (!authorizationResult) {
              throw new NotAuthorizedException(UNAUTHORIZED_STRING + method.getName());
            }
          }

          return method.invoke(target, (Object[]) null);
        } catch (EntryDestroyedException e) {
          // eat the Exception
          return QueryService.UNDEFINED;
        } catch (IllegalAccessException e) {
          throw new NameNotFoundException(
              String.format(
                  "Method ' %s ' in class ' %s ' is not accessible to the query processor",
                  m.getName(), target.getClass().getName()),
              e);
        } catch (InvocationTargetException e) {
          // if the target exception is Exception, wrap that,
          // otherwise wrap the InvocationTargetException itself
          Throwable t = e.getTargetException();
          if ((t instanceof EntryDestroyedException)) {
            // eat the exception
            return QueryService.UNDEFINED;
          }
          if (t instanceof Exception)
            throw new QueryInvocationTargetException(t);
          throw new QueryInvocationTargetException(e);
        }
      } else {
        try {
          return ((Field) m).get(target);
        } catch (IllegalAccessException e) {
          throw new NameNotFoundException(
              String.format(
                  "Field ' %s ' in class ' %s ' is not accessible to the query processor",
                  m.getName(), target.getClass().getName()),
              e);
        } catch (EntryDestroyedException e) {
          return QueryService.UNDEFINED;
        }
      }
    } catch (EntryDestroyedException e) {
      // eat the exception
      return QueryService.UNDEFINED;
    }
  }

  public  AttributeDescriptor(TypeRegistry pdxRegistry, String name);

}