jsr372-experts@javaserverfaces-spec-public.java.net

[jsr372-experts] Re: f:validateWholeBean copier strategy

From: arjan tijms <arjan.tijms_at_gmail.com>
Date: Sat, 12 Dec 2015 00:12:54 +0100

Hi,

On Fri, Dec 11, 2015 at 10:44 PM, Edward Burns <edward.burns_at_oracle.com>
wrote:

> I'm ok with changing the hard coded strategy, but I feel making the
> strategy customizable is too much complexity. Such customizability is
> better suited to an extension library rather than being part of the core
> spec.
>

I personally think having functionality pluggable / decorateable has been
one of the biggest strengths of JSF since its inception. There are
basically two aspect to the customisation here:

1. Choose between the existing strategies that are already there
2. Use a user provided strategy



> Can you please suggest an alternate private implementation? I'm happy
> to have that code rewritten.
>

Sure, the idea is that the method is now used as follows:

  ValueExpression beanVE = c.getValueExpression("value");
  Object val = beanVE.getValue(context.getELContext());
  Object valCopy = copyObjectAndPopulateWithCandidateValues(beanVE, val,
candidate);

This can become:

  ValueExpression beanVE = c.getValueExpression("value");
  Object val = beanVE.getValue(context.getELContext());
  String copierName = (String) c.getAttributes().get("copier");
  Object valCopy = copyObjectAndPopulateWithCandidateValues(context,
beanVE, val, copierName, candidate);


Then the copyObjectAndPopulateWithCandidateValues method can become:

  private Object copyObjectAndPopulateWithCandidateValues(
            FacesContext context, ValueExpression beanVE,
            Object val, String copierName
            Map<String, Map<String, Object>> candidate) {
        // <editor-fold defaultstate="collapsed">

        // Populate the value copy with the validated values from the
candidate
        Map<String, Object> propertiesToSet = new HashMap<>();
        for (Map.Entry<String, Map<String, Object>> cur :
candidate.entrySet()) {
            propertiesToSet.put(cur.getKey(), cur.getValue().get("value"));
        }

        // Copy the value so that class-level validation can be performed
        // without corrupting the real value
        Object valCopy = getCopier(context, copierName).copy(val);

        if (null == valCopy) {
            throw new FacesException("Unable to copy value from " +
beanVE.getExpressionString());
        }

        ReflectionUtils.setProperties(valCopy, propertiesToSet);
        return valCopy;

        // </editor-fold>
    }


The private implementation of getCopier() could then be:

private static Copier getCopier(FacesContext context, String copierName) {
Copier copier = null;

if (!isEmpty(copierName)) {
Object expressionResult = evaluateExpressionGet(context, copierName);

if (expressionResult instanceof Copier) {
copier = (Copier) expressionResult;
}
else if (expressionResult instanceof String) {
copier = instance((String) expressionResult);
}
}

if (copier == null) {
copier = new MultiStrategyCopier();
}

return copier;
}

In the above method, evaluateExpressionGet() is a utility method for
context.getApplication().evaluateExpressionGet and instance() for
~Class.forName(className, true,
Thread.currentThread().getContextClassLoader())

Of course the code can still be different on details, but that would be the
general idea. All the methods involved can of course stay private.

Hope this makes it more clear.

Kind regards,
Arjan Tijms






>
> Thanks,
>
> Ed
>
> --
> | edward.burns_at_oracle.com | office: +1 407 458 0017
>