Программное создание экземпляров ValueExpressions

Я динамически добавляю компоненты в PreRenderViewEvent, используя следующий метод http://blog.kennardconsulting.com/2010/10/safely-manipulating-component-tree-with.html .

Он отлично работает для части добавления компонентов, но у меня возникает проблема, когда я пытаюсь динамически создавать экземпляры ValueExpression-s.

Чтобы быть более конкретным, у меня возникает проблема, когда я пытаюсь создать динамическое выражение ValueExpression, используя динамически передаваемый параметр.

Попробуем объяснить на примере...

На верхнем уровне я использую компонент тега (компонент, описанный в файле тега, не составной и не пользовательский компонент.

<my:topComponent param=#{toto}"/>

В my:topComponent я передаю параметр вложенному компоненту.

<my:nestedComponent param2=#{param}/>

Этот вложенный компонент использует пользовательский компонент (в моем случае компонент, который я получаю из таблицы данных Primefaces), передавая ему param2 в качестве другого параметра...

<my:customComponent finalParam=#{param2}/>

В customComponent я динамически добавляю некоторые дочерние компоненты в PreRenderViewEvent и устанавливаю некоторые выражения ValueExpression для customComponent.

Некоторые из этих выражений используют finalParam. Итак, я разворачиваю значение finalParam, затем создаю новое выражение ValueExpression:

String varName = getValueExpression("finalParam").getExpressionString().replace("#{", "").replace("}", "");

Затем я создаю экземпляр выражения динамического значения, используя эту вспомогательную функцию:

public static ValueExpression createValueExpression(String expression, Class clazz) {
        FacesContext fc = FacesContext.getCurrentInstance();
        ELContext elContext = fc.getELContext();
        ExpressionFactory expFactory = fc.getApplication().getExpressionFactory();
        ValueExpression ret = expFactory.createValueExpression(elContext, expression, clazz);
        return ret;
    }

Пример :

ValueExpression dynExpression = JSFUtils.createValueExpression("#{" + varName + ".code" + "}"), Object.class);

В этом примере выражение значения — «#{param2.code}».

Затем я могу установить это valueExpression для моего компонента:

this.setValueExpression("rowKey", dynExpression);

Весь этот код находится в классе пользовательского компонента. Я использую рендерер базового класса.

Однако программно созданный экземпляр ValueExpression не оценивается должным образом во время отрисовки. Например, когда модуль рендеринга с данными Primefaces пытается вычислить rowKey, #{param2.code} оценивается как "null", так как param2 кажется неизвестным.

Что я должен сделать, чтобы исправить это? Во время отладки я заметил, что getValueExpression("finalParam") имеет набор VariableMapper, а dynExpression не имеет ни одного (нулевое значение)

Если я правильно понял, этот VariableMapper используется для перевода param2 в param.

Как я могу создать экземпляр своего динамического выражения, чтобы сохранить цепочку VariableMapper(s)? Тот же вопрос для FunctionMapper.

Заранее спасибо.

ОБНОВЛЕНИЕ Я согласен с ответом Ричарда Кеннарда: похоже, это та же ошибка.

Поскольку я не могу ждать исправления годами, я использую следующий кладж для рекурсивного разрешения переменных. Это работает для простых случаев с моим стеком MyFaces 2.1.9 / CODI 1.0.5 / OWB 1.1.6 / Tomcat 7.

    public static String getValueExpressionExpression(ValueExpression valueExpression) {
        return valueExpression.getExpressionString().replace("#{", "").replace("}", "");
    }

    public static String getMappedValueExpression(ValueExpression valueExpression) {
        ContextAwareTagValueExpression ctxAware = (ContextAwareTagValueExpression)valueExpression;
        if(ctxAware != null) {
            return getMappedValueExpression((WrappedValueExpression)ctxAware.getWrapped());
        }
        return getValueExpressionExpression(valueExpression);
    }

    public static String getMappedValueExpression(WrappedValueExpression wrappedExpression) {
        String exprString = wrappedExpression.getExpressionString().replace("#{", "").replace("}", "");
        String ret = exprString;
        try {

            Field valueExpression = WrappedValueExpression.class.getDeclaredField("valueExpression");
            valueExpression.setAccessible(true);
            ValueExpressionImpl vei = (ValueExpressionImpl) valueExpression.get(wrappedExpression);
            Field varMapper = ValueExpressionImpl.class.getDeclaredField("varMapper");
            varMapper.setAccessible(true);
            VariableMapperImpl vmi = (VariableMapperImpl) varMapper.get(vei);
            if(vmi != null) {
                String[] components = exprString.split("\\.");
                components[0] = getMappedValueExpression(vmi.resolveVariable(components[0])); 
                ret = "";
                for(int i = 0 ; i < components.length ; i++) {
                    if(i != 0) {
                        ret += ".";
                    }
                    ret += components[i];
                }
            }
        } catch (Exception ex) {
            logger.error("Exception lors du mapping de l'expression EL " + exprString, ex);
        } finally {
            return ret;
        }
    }

Было бы здорово иметь более чистые версии этого обходного пути в MyFaces или Mojarra...


person Ludovic Pénet    schedule 09.08.2013    source источник


Ответы (1)


Это известная ошибка в JSF.

Он был отправлен сюда команде Mojarra: https://java.net/jira/browse/JAVASERVERFACES-2089 и здесь с командой MyFaces: https://issues.apache.org/jira/browse/MYFACES-3168. Я понимаю, что формулировка этих отчетов об ошибках не совсем то, что вы ожидаете, но это та же самая ошибка. Если вы посмотрите на комментарии:

Hi guys,

I just hit this bug again while working on a client's code, and
noticed Manfred had closed it as 'resolved'?

Why was this considered 'resolved'? It's a real problem for me,
stopping me doing things like...

<h:dataTable value="#{companyImport.importFiles}" var="_importFile">
    <h:column>
        <h:outputText value="#{_importFile.name}:"/>
    </h:column>
    <h:column>
        <m:metawidget value="#{_importFile.import}"/>
    </h:column>
</h:dataTable>

...because the m:metawidget cannot 'see' the #{_importFile} var.

Can we please reopen, or at least explain why it is resolved?

Ни один из этих отчетов об ошибках не приносит большого прогресса. Вы можете прокомментировать их, проголосовать за них и придать вес проблеме.

person Richard Kennard    schedule 09.08.2013
comment
Да, похоже, это тот же баг. Я также добавлю комментарий к проблеме MyFaces, возможно, предлагаю чистую реализацию кладжа, который я предоставлю в качестве обходного пути через несколько минут. - person Ludovic Pénet; 12.08.2013