PrimeFaces UIMenuItem 对象获取无效 ID

PrimeFaces UIMenuItem objects get invalid IDs

我目前正在执行一项任务,涉及将项目的 PrimeFaces 库从 PrimeFaces 3.2 更新到 6.0。

从 3.5 版到 4.0 版,API change in PrimeFaces' MenuModel 不向后兼容。

我正在处理的代码使用了扩展 javax.faces.component.UICommand.

的 class org.primefaces.component.menuitem.MenuItem

从 PrimeFaces 4.0 开始,org.primefaces.component.menuitem.MenuItem 是一个接口,并且有一个新的 class org.primefaces.component.menuitem.UIMenuItem 实现了该接口并扩展了 javax.faces.component.UICommand。新的 UIMenuItem class 与旧的 MenuItem class 具有或多或少相同的接口,至少它支持我正在处理的代码中使用的所有方法.

很明显,我更改了 MenuItem 对象的实例化代码以利用新的 UIMenuItem class。这样做,代码编译没有错误,但在运行时失败并出现以下异常:

Dec 01, 2016 11:58:02 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet Faces Servlet threw exception
java.lang.IllegalArgumentException: 0
    at javax.faces.component.UIComponentBase.validateId(UIComponentBase.java:542)
    at javax.faces.component.UIComponentBase.setId(UIComponentBase.java:363)

在调试器中,我可以看到实际上有 UIMenuItem 个 ID 为 0 的对象。

UIMenuItem 个对象都是通过如下所示的工厂方法创建的:

public static MenuItem createNavigationMenuItem() {
    UIMenuItem item = new UIMenuItem();
    item.setStyle("padding: 0;");
    item.setUpdate(":tableForm,:navForm");
    item.addActionListener(actionListener);
    return item;
}

事情是这样的。 UIMenuItem 对象的 ID 由 org.primefaces.model.menu.BaseMenuModel:

中的代码设置
public void generateUniqueIds() {
    this.generateUniqueIds(getElements(), null);
}

private void generateUniqueIds(List<MenuElement> elements, String seed) {
    if(elements == null || elements.isEmpty()) {
        return;
    }

    int counter = 0;

    for(MenuElement element : elements) {
        String id = (seed == null) ? String.valueOf(counter++) : seed + ID_SEPARATOR + counter++;
        element.setId(id);

        if(element instanceof MenuGroup) {                
            generateUniqueIds(((MenuGroup) element).getElements(), id);
        }
    }
}

我是这样读这段代码的:当调用generateUniqueIds()时,elements参数引用的MenuElements必然会得到012,等等

现在请看一下javax.faces.component.UIComponentBase中的这个方法:

private static void validateId(String id) {

    if (id == null) {
        return;
    }
    int n = id.length();
    if (n < 1) {
        throw new IllegalArgumentException("Empty id attribute is not allowed");
    }
    for (int i = 0; i < n; i++) {
        char c = id.charAt(i);
        if (i == 0) {
            if (!Character.isLetter(c) && (c != '_')) {
                throw new IllegalArgumentException(id);
            }
        } else {
            if (!Character.isLetter(c) &&
                    !Character.isDigit(c) &&
                    (c != '-') && (c != '_')) {
                throw new IllegalArgumentException(id);
            }
        }
    }
}

可以清楚地看到,以数字开头的 ID 总是会导致 IllegalArgumentException,而这正是我的代码失败的地方!

现在我想知道:为什么 PrimeFaces 库中的代码会为 MenuElement 对象提供明显无效的(根据它所构建的 JSF 代码)id?

First Rule of Programming 之后,我确信 PrimeFaces 代码没有问题,我的困惑是因为我缺乏理解。但是,BaseMenuModel 中的代码对我来说仍然毫无意义。有没有人可以帮我弄清楚我没有得到什么?

不能将 MenuModel 与 UIMenuItems 混合使用:https://github.com/primefaces/primefaces/issues/199