2014年1月19日 星期日

Dynamic height for each different row of RichFaces' ExtendedDataTable


Problem:

When the data in the column is many, it simply take up to multiple lines but stuff are hidden behind because of the default row height












We have issues when the size of result from getDataList() in bean.java is more than one...
<rich:column label="Details" >
       <f:facet name="header">Details</f:facet>
              <a4j:repeat rowKeyVar="rowKey"
                    value="#{bean.getDataList(resourceVar.instanceId)}"    
                    var="beanData">         
                    <a4j:commandLink actionListener="#{bean.someNavigateAction}">
                       <h:graphicImage  value="xxx.gif" />                              
                    </a4j:commandLink>        
                    <h:outputText  value="#{resourceVar.instanceId}-#{rowKey}" />      
            </a4j:repeat>
</rich:column>


(check the field with FireBug, some of the fields are hidden)













Solution:

Display a link on those grids which has hidden info, user can click the link to expand/collapse the whole row to navigate complete information.















Code:

Add styleClass named 'referenceLink' for the commandLink, since the number of commandLinks in a field vary from row to row. Then we we would be able to count them via jQuery selector. And, we add a 'More' commandLink for user to click  in order to expand/collapse the field.

.xhtml

 <h:form id="resultForm1">

        <rich:extendedDataTable id="table" 
          var="resourceVar"          
          value="#{bean.resourceDataModel}"   
          rendered="#{not empty bean.resourceDataModel}" 
          rowKeyVar="row" >

          .... other columns here     



         <rich:column label="Details">

              <f:facet name="header">Details</f:facet>             
              <a4j:repeat rowKeyVar="rowKey" 
                    value="#{bean.getDataList(resourceVar.instanceId)}"               
                    var="beanData">         
                      <a4j:commandLink styleClass="referenceLink"  actionListener="#{bean.someNavigateAction}"  > <h:graphicImage  value="xxx.gif" />                              

                      </a4j:commandLink>                   
                       <h:outputText  value="#{resourceVar.instanceId}-#{rowKey}" />                           
                       <a4j:commandLink value="More" rendered="#{rowKey==0}" styleClass="expandLink" onclick="expandRow('#{resourceVar.instanceId}');"  />         
            </a4j:repeat>


         </rich:column>

</h:form>


javascript

 // invoke right after the extendedDataTable appears, remove unnecessary 'More' expand link from each row     
function adjustResultTable(){                 
             jQuery("table[id^='resultForm1']").find("tr[id^='resultForm1']").each(function(){     
             var numOfLinks = jQuery(this).find("a[class='referenceLink']").length; // how many items need to be displayed            
              if(numOfLinks<2) // doesn't need to show 'More' expand link'                
                   jQuery(this).find("a[class='expandLink']").hide();         
              });     
}      

var itemHeight = 20; // height in px for one row     
function expandRow(rowId){   
       var thisRow = jQuery("table[id^='resultForm1']").find("tr[id*='"+rowId+"'][id^='resultForm1']");        
       var expandlink = jQuery(thisRow).find("a[class='expandLink']");        
        if(expandlink[0]){     
               if(expandlink[0].innerHTML=='More'){ // expand                
                    var numOfLinks = jQuery(thisRow).find("a[class='referenceLink']").length; // how many items need to be displayed                 
                    if(numOfLinks>1){                     
                        var rowHeight = parseInt(numOfLinks*itemHeight);                     
                        jQuery(thisRow).find("td").each(function(){ // adjust the height of each grid                        
                              jQuery(this).find("div").first().css({'height':rowHeight+'px'});                    
                        });                 
                     }                 
                    expandlink[0].innerHTML = 'Less';            
               }             
               else{ // collapse                 
                    var rowHeight = itemHeight;                 
                    jQuery(thisRow).find("td").each(function(){ // adjust the height of each grid                    
                        jQuery(this).find("div").first().css({'height':rowHeight+'px'});                 
                    });                 
                    expandlink[0].innerHTML = 'More';             
               } // end of else        
          } // end of if(expandLink[0])
}      

Other notes:
1,It doesn't work well if we simply adjust the height on the <tr> level












2.In our expandRow() javascript function, we adjust the height of each first div inside <td> inside the row step by step:
(the first td)
(the second td)

(all the height of div inside td are changed, then the expansion is finished)


3.jQuery("table[id^='resultForm1']").find("tr[id*='"+rowId+"'][id^='resultForm1']");   

I happened to notice that, for the id of <tr> for each row, the prefix would be like "resultForm1:table:xxxxx": resultForm is the id of <h:form>, table is the id of <rich:extendedDataTable>, and xxxxx would be the instanceId of the data this row represent. So we can take advantage of this pattern in javascript to find the correct row to expand/collapse.



4.This solution might look like a workaround, but I couldn't found any existing built-in solution in RichFaces extendedDataTable. The following links are the related discussion I have traced on the internet:


https://community.jboss.org/thread/231328 http://stackoverflow.com/questions/14258514/richfaces-extendeddatatable-header-size-mismatches-column-size-when-set-to-auto 
https://community.jboss.org/thread/170991https://github.com/richfaces4/components/blob/master/iteration/ui/src/main/resources/META-INF/resources/org.richfaces/extendedDataTable.js#L309
 https://community.jboss.org/thread/170991 
http://stackoverflow.com/questions/963971/how-to-conditionally-style-a-row-in-a-richdatatable
 
https://community.jboss.org/thread/234159?_sscc=t
  


2013年10月18日 星期五

The powerful text search utility in Linux - Grep


Since we use Seam as our framework to develop web applications, we adopt its mechanism of securing JSF pages to restrict access to users that aren't authenticated, which is to add the login-required attribute to the .page.xml file.  (Section 11.2.2 in 'Seam in Action' )

<page xmlns="http://jboss.com/products/seam/pages"

      login-required="true"    
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd">
...
</page>
However, one day, we suddenly noticed there were some .page.xml files didn't include that authentication attribute, and this might introduce security issue to our system. We needed to find all those files and correct them, but they distributed over all the directories which was not easy to find, at least I didn't think I could achieve that in a fast way by JBoss Developer Studio, manually checking through all the .page.xml files (hundreds of them) in the workspace obviously was not a good idea.

Therefore, I tried to check if our text search utility in Linux - grep can solve my problem:
I want to search all the .page.xml files under webapp directory recursively, and get a list of filename which doesn't contains 'login-required' string. 

I thought I needed to use several pipelines, or applied regression expression which I'm not very good at.
After surveyed for a while, fortunately, things didn't get that complicated. One simple line can serve my need:

[root@localhost webapp]# pwd
/root/workspace/proj/target/proj/proj-war/src/main/webapp
[root@localhost webapp]# grep -RL 'login-required=\"true' --include='*.page.xml' .
Within 1 second, the screen started to print out the results I wanted, thanks for this powerful "Global Regular Expression Print"!

tag note for the grep:
       --include=GLOB
              Search only files whose base name matches GLOB (using wildcard  matching  as
              described under --exclude).

       -L, --files-without-match
              Suppress normal output; instead print the name of each input file from which
              no output would normally have been printed.  The scanning will stop  on  the
              first match
.

       -l, --files-with-matches
              Suppress normal output; instead print the name of each input file from which
              output would normally have been printed.  The  scanning  will  stop  on  the
              first match.  (-l is specified by POSIX.)

       -R, -r, --recursive
              Read  all files under each directory, recursively; this is equivalent to the
              -d recurse option.

2013年6月30日 星期日

Testing display the code nicely via Google Code Prettify

Here is the result:

int sum=0;  
for(int i=0;i<10;i++){
  sum+=i;
} 
System.out.println("total:"+sum);


What I did basically was according to stackoverflow: How to use prettify with blogger/blogspot?  I took the solution provided by SK9 because it looked simpler.

However, since the posts was three years ago, now the Blogger setting UI is slightly different, let me post my steps here.

Step1:Login blogger with your account, select 'Template', and click on 'Edit HTML' button.




Step2:
Copy the following two lines to the block inside <head></head>, and add onload='prettyPrint()' in <body> tag.




Step3: When post an article, switch to 'HTML' mode, and wrapper your code with <pre class="prettyprint"> and </pre>, you will see the effect!




Nevertheless, looks like wordpress has this function as built-in, and the appearance is quite decent and clear.

(btw, I have also found another tutorial in Chinese: 
http://blog.cookys.net/2012/10/blogger-google-code-prettify.html, the article was not too long ago)

2013年1月9日 星期三

The evil getter and setter 萬惡的getter和setter


今天在Stackoverflow上讀到了一篇文章 Are Getters and Setters evil?  (getter跟setter真的那麼邪惡嗎?)

把一些重點摘錄如下,有習慣看英文的可以直接去看這邊: http://stackoverflow.com/questions/565095/are-getters-and-setters-evil

其中一個大原則是,針對private field, 不使用public setter去直接改值, 用其他有意義且必要的metohd去改變值

範例:  使用kill()或者是damaget()這兩個method去對private的hp欄位進行值的更改,而不使用setHp()來修改
(發表者Zarkonnen)
private int hp; // Set in constructor.
public boolean isAlive() { return hp > 0; } // Same method signature.
public void kill() { hp = 0; } // Same method signature.
public void damage(int damage) { hp -= damage; }


而另一位Jon Skeet網友則如此定義邪惡的等級 (或者說是code的好壞/安全性/OO觀念):

Very evil: public fields.
Somewhat evil: Getters and setters where they're not required.
Good: Getters and setters only where they're really required - make the type expose "larger" behaviour which happens to use its state, rather than just treating the type as a repository of state to be manipulated by other types.
不確定我是否理解正確,他覺得最糟的狀況是直接使用public field (比不必要的getter/setter還糟),好的狀況則是在必要的狀況下使用getter及setter,而所謂必要的狀況,應該是有一些額外的行為想要在getter及setter裡面做,而非僅僅取值/設值.

那除了取值/設值外,會有什麼"額外的行為"是需要在getter和setter裡面做的呢?
 coobird網友提供了兩個例子:
範例1: 計算值遭到修改的次數   (更常見的狀況可能是驗證使用者所輸入的資料)
 public void setValue(int value)
{
    this.value = value;
    count++;
}
範例2: 保護原值不任意遭到修改
public int[] getArray()
{
    return myArray.clone();
}
ImmutableArray a = new ImmutableArray();
int[] b = a.getArray();
b[0] = 10;      // 不會真正更改到myArray本身

而也有人提出 (網友nathan),如果在setter裡面做了許多多餘的動作,別人在使用此API時並無法得知, 如果每個setter使用者都要去確認相當麻煩。
像是這樣的method會是個壞例子

public void setAlive(boolean isAlive){
    this.isAlive = isAlive;
    if (isAlive)
        addToWorld(this);
    else
        removeFromWorld(this);
}
建議改成在setter外的method做,如:
public void kill(){
    isAlive = false;
    removeFromWorld(this);
}

還有一篇裡面有人引用到的來自Java World的文章,作者為Allen Holub,還沒讀完,等讀完再一併整理...  (天啊..看日期是九年前發表的文章的意思嗎...)
http://www.javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html






2012年12月19日 星期三

My site got hacked!

From Window to Linux

There are about 8 members in my developing team, some use Windows while some use Linux to develop. One day, our boss (project leader) told us it's time to migrate to Linux for some consistency purpose which can also avoid unnecessary redundancy testing process in both system.

Later on, we four junior engineers who use Windows spent almost two days struggling with this unfamiliar system (what we applied is CentOS 6.3). Before I only knew few basic commands such as use ls -l to view the list of directories/files and use cd to change directory...what a newbie in Linux.