没有比人更高的山

一个隐藏了将近2年的Bug1

最近在维护华中科技大学电信系的电子档案库,发现了一个隐藏了将近2年的Bug,记录如下:

需求:一个科研项目有一定的工作量,而每个项目有很多参与人,需要为每个参与人设置其工作量分配额度。当用户添加了一个项目时,需要同时添加其参与人:

实现:当用户录入了科研项目信息之后,点击“添加参与人”的按钮,转向到选择参与人的页面,可以勾选任意数目的参与人,点击“保存”按钮后返回科研项目信息页面。

在执行保存选择的参与人信息时,检查所选择的人员是否已经在科研项目的参与人中存在的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
for (Iterator<string> iterator = selectedMemberList.iterator(); iterator.hasNext();) {
    ParticipatorBean participatorBean = new ParticipatorBean();
    long teacherId = Long.parseLong(iterator.next());
    Teacherbasicinfo teacher = ServiceProvider.getTeacherInfoService().findById(teacherId);
    Long id = teacher.getTeacherId();
    participatorBean.setTeacherId(id);
    participatorBean.setTeacherName(teacher.getTeacherName());
    if (participatorsMap.get(id) == null) {
        participatorsMap.put(String.valueOf(id), String.valueOf(id));
        participators.add(participatorBean);
    }
}
</string>

其中,selectedMemberList是选中的用户列表,ParticipatorBean是用来传递给前一个页面的参与人的Java类,Teacherbasicinfo是领域模型,代表教师基本信息,老师编号teacherId为Long型。

participatorsMap是我从来去除重复的一个HashMap,也就是说我每遍历一个Teacherbasicinfo,就将其teacherId转换成字符串型添加到这个Map中,在每次添加之前我先检查这个Map是否已经存在了(且不论这种方法的好与坏,这是我们几年前的代码了)

这段代码无编译异常、无运行异常,但是却有逻辑异常。有用户反映。多次选择的时候会有重复的项出现。

经过多番调试,最后发现第八行代码有误,应该将

1
if (participatorsMap.get(id) == null) {

改成为:

1
if (participatorsMap.get(String.valueOf(id)) == null) {

恍然大悟,虽然我使用的是范型,但是Map的get方法仍然接受的是Object类型的参数,不对其参数进行检查,其put方法的签名为V put(K key, V value),则会对key和value的类型都进行检查。

这是我的错呢,还是Java的错,为啥不把get方法的参数用范型类型检查一下呢?可能是sun的工程师有别的考虑吧。

VN:F [1.7.5_995]
Rating: 10.0/10 (1 vote cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)

好用的Wordpress代码高亮插件WP-Syntax1

开博几个月以来一直没有使用代码高亮插件,并不是我不想,而是找到的几个代码插件实在是太让人失望:有些很安装使用很复杂,让人摸不到头脑;有些效果太夸张,界面不够简单整洁;还有些用js来实现,但是遇到代码中有&gt;(大家都知道这是>的转义字符)这样的代码就出错了,而我有很多代码经过Wordpress的处理有些尖括号已经被转义字符替代了,有些XML代码就更厉害,通篇都是&gt; &lt;,让人头疼不已。

今天偶然发现一个插件,名为WP-Syntax,顿时让我眼睛一亮,别人都说它是兼容性最好的代码高亮插件,经过试用,果不其然,我已经在本博客上全面启用这个插件了。

使用方法其实很多,不妨我罗嗦两句,兼凑个字数,也算做个笔记:

1
<pre lang="java" line="1" escaped="true">//Java代码</pre>

其中标签pre的属性lang表示语言类型line表示显示起始代码行号,如果不设置则不显示行号,escaped表示是否代码是否为转义字符,默认为false,即如果你的代码中有转义字符,则需要设置escaped=”true”

VN:F [1.7.5_995]
Rating: 0.0/10 (0 votes cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)

Java int型转换中文大写数字表示方法0

之前写过一篇博客,记录的是如何将Java中文大写数字转int型,今天遇到的问题是将Java int型转换成中文大写数字表示。

下面是程序,很容易理解,不过,只支持10000以内的数字转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static String intToChnNumConverter(int num){
    String resultNumber = null;
    if(num > 10000 || num < 0){
        return "";
    }
    HashMap chnNumbers = new HashMap();
    chnNumbers.put(0, "零");
    chnNumbers.put(1, "一");
    chnNumbers.put(2, "二");
    chnNumbers.put(3, "三");
    chnNumbers.put(4, "四");
    chnNumbers.put(5, "五");
    chnNumbers.put(6, "六");
    chnNumbers.put(7, "七");
    chnNumbers.put(8, "八");
    chnNumbers.put(9, "九");
 
    HashMap unitMap = new HashMap();
    unitMap.put(1, "");
    unitMap.put(10, "十");
    unitMap.put(100, "百");
    unitMap.put(1000, "千");
    int[] unitArray = {1000, 100, 10, 1};
 
    StringBuilder result = new StringBuilder();
    int i = 0;
    while(num > 0){
        int n1 = num / unitArray[i];
        if(n1 > 0){
            result.append(chnNumbers.get(n1)).append(unitMap.get(unitArray[i]));
        }
        if(n1 == 0){
            if(result.lastIndexOf("零") != result.length()-1){
                result.append("零");
            }
        }
        num = num % unitArray[i++];
        if(num == 0){
            break;
        }
    }
    resultNumber = result.toString();
    if(resultNumber.startsWith("零")){
        resultNumber = resultNumber.substring(1);
    }
    if(resultNumber.startsWith("一十")){
        resultNumber = resultNumber.substring(1);
    }
    return resultNumber;
}
VN:F [1.7.5_995]
Rating: 0.0/10 (0 votes cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)

一些国人开发的好用JS控件0

以前都是用国外开发的像FCKEditor(已经更名为CKEditor)、Flexigrid等,很多英文文档看得头大,而且代码不一定写得很好,比如Flexigrid,我找到了至少2处bug,开发中很多时间都花费在看那上千行的js脚本上了。这里介绍几款国人开发的js控件,非常好用

My97日期控件:http://www.my97.net/dp
好处:可以选择时间,可以显示周,自定义格式,有多种皮肤可供选择,选择老年份非常方便:

KindEditor: http://www.kindsoft.net
一款可见即所得编辑器,用起来非常简单,相比CKEditor和TinyMCE要简单很多,特别是TinyMCE一大堆插件,一看头就晕了,项目开发期紧张,哪有那么多时间去看这些个插件是干嘛的哟。
支持两种皮肤,其中一种就是TinyMCE的皮肤,呵呵。

基于JQuery的Inline Editor插件http://www.wbfsaworkstation.com.cn
实在是不好意思,我不知道inline这个词怎么用中文来表达,我想每个开发人员应该心里明白是啥意思吧。支持单选按钮、多选列表、文本域、软键盘等,还有评分插件以及日历插件(日历插件基于My97)

如果遇到好的,会继续推荐,呵呵…

VN:F [1.7.5_995]
Rating: 0.0/10 (0 votes cast)
VN:F [1.7.5_995]
Rating: +1 (from 1 vote)

JSF中CommandButton与CommandLink传值0

f:param标签能够将一个参数添加到组件。需要注意的是f:param标签的不同表现依赖于它所关联的组件类型

【1】如果为 h:outputText添加f:param标签,那么JSF实现将使用参数来填充占位符,例如{0}、{1}等。

【2】如果添加f:param标签到h:commandLink,JSF实现会将参数值作为请求参数传递到服务器,如:

1
2
3
<h :commandLink actionListener="#{userListBean.checkUser}" value="审核通过">
    <f :param name="userId" value="#{user.userId}" />
</h>

在服务器端可以使用如下方法来获取传递到服务器端的值:

1
2
3
4
private void checkUser(ActionEvent actionEvent){
    String uid = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("userId");
    // other code
}

但是f:param的传值方式对于h:commandButton是没有作用的,(如果你是用搜索引擎搜到这篇文章的话,相信你肯定是遇到了这个问题),详情可以参考http://www.javaeye.com/topic/93388

如果是h:commandButton,那么可以使用f:attribute来进行传值,示例如下:

1
2
3
<h :commandButton actionListener="#{userListBean.resetPassword}" value="审核通过">
    <f :attribute name="userId" value="#{user.userId}"/>
</h>

在服务器端可以使用如下方法来获取传递到服务器端的值:

1
2
3
4
private void resetPassword(ActionEvent actionEvent){
    long userIdString = (Long) actionEvent.getComponent().getAttributes().get("userId");
    //other code ……
}
VN:F [1.7.5_995]
Rating: 0.0/10 (0 votes cast)
VN:F [1.7.5_995]
Rating: 0 (from 0 votes)