sâmbătă, 21 martie 2015

Expose a path as an OmniFaces tree (<o:tree>)

In this post you can see a use case for OmniFaces <o:tree>. This is a versatile component that allows us to put almost anything in a tree structure (e.g. see the editable tree). Next, we want to obtain a tree with the following features:

·         render tree as a folder tree style
·         collapse/expand folders by clicking on folder's icons (AJAX based)
·         select files by click the file name (AJAX based)

First, we write a simple class to encapsulate information about each file/folder. This class is named ItemEntity and is listed below (in order to list folder first, you need to define a custom compareTo()):

package beans;

import java.io.Serializable;
import java.util.Objects;

public class ItemEntity implements Serializable, Comparable {

  private static final long serialVersionUID = 1L;

  private String path;      // the absolute path of the folder/file
  private String name;      // the file/folder name
  private String size;      // the file size in bytes
  private String type;      // the artifact type: folder or file
  private boolean visible;  // specific to files for indicating visibility
  private boolean expanded; // specific to folders for indicating collapse/expand

  public ItemEntity() {
  }

  // for a folder
  public ItemEntity(String path, String name, String type, boolean expanded, boolean visible) {
    this.path = path;
    this.expanded = expanded;
    this.visible = visible;
    this.name = name;
    this.type = type;
  }

  // for a file
  public ItemEntity(String path, String name, String type, String size, boolean visible) {
    this.path = path;
    this.visible = visible;
    this.name = name;
    this.type = type;
    this.size = size;
  }

  public boolean isVisible() {
    return visible;
  }

  public void setVisible(boolean visible) {
    this.visible = visible;
  }      

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getSize() {
    return size;
  }

  public void setSize(String size) {
    this.size = size;
  }

  public String getType() {
    return type;
  }

  public void setType(String type) {
    this.type = type;
  }  

  public boolean isExpanded() {
   return expanded;
  }

  public void setExpanded(boolean expanded) {
    this.expanded = expanded;
  }   

  public String getPath() {
    return path;
  }

  public void setPath(String path) {
    this.path = path;
  }       
   
  @Override
  public int hashCode() {
    int hash = 7;
    hash = 29 * hash + Objects.hashCode(this.name);
    return hash;
  }

  @Override
  public int compareTo(Object o) {
    ItemEntity it = (ItemEntity) o;
    if (it.getType().equals("folder")) {
        return 1;
    } else {
        return -1;
    }
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final ItemEntity other = (ItemEntity) obj;
   if (!Objects.equals(this.name, other.name)) {
       return false;
   }
   return true;
  }

  @Override
  public String toString() {
   return name + "(" + size + ")";
  }
}

Next, we can use a recursive approach to traverse each folder/file of the given path. Each file/folder will become an instance of ItemEntity and it will be added in a SortedTreeModel (one of the TreeModel implementations). In addition, we need a method responsible to show/hide a folder content when the user expand (default)/collapse a folder. So, next, we have a write a bean (TreeBean) that uses the ItemEntity for representing each artifact obtained via a recursive method (simple folder traversal in a recursive approach). Each ItemEntity is placed in the SortedTreeModel. Moreover the showHideItems() methods is responsabile to show/hide a folder content (node tree children):

package beans;

import java.io.File;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import org.omnifaces.model.tree.SortedTreeModel;
import org.omnifaces.model.tree.TreeModel;
import org.omnifaces.util.Faces;

@Named
@ViewScoped
public class TreeBean implements Serializable {
   
  private static final long serialVersionUID = 1L;
   
  private TreeModel<ItemEntity> tree;
  private String selected = "none";
   
  @PostConstruct
  public void init() {
    tree = new SortedTreeModel<>();
    // current application folder
    //walk(Faces.getRealPath("."), tree);
    // some local folder
     walk("D:\\Omnifaces\\blog", tree);
  }
   
  public TreeModel<ItemEntity> getTree() {
   return tree;
  }
   
  public void showHideItems(TreeModel<ItemEntity> node) {
    ItemEntity folderData = node.getData();
    for (TreeModel<ItemEntity> children : node.getChildren()) {
         ItemEntity fileData = children.getData();
         fileData.setVisible(!folderData.isExpanded());
    }
    folderData.setExpanded(!folderData.isExpanded());            
  }
   
  public void selectedFile(String selected){
    this.selected = selected;
  }

  public String getSelected() {
    return selected;
  }
   
  private void walk(String path, TreeModel<ItemEntity> treem) {
      
    File root = new File(path);
    File[] list = root.listFiles();
       
        if (list == null) {
            return;
        }
       
        for (File f : list) {
             if (f.isDirectory()) {
                 System.out.println("Directory: " + f.getName());
                 walk(f.getAbsolutePath(),treem.addChild(new ItemEntity(f.getAbsolutePath(), f.getName(),"folder",true,true)));
             } else {
                 treem.addChild(new ItemEntity(f.getAbsolutePath(),f.getName(),"file"," (" + String.valueOf(f.length())+" bytes)", true));
                 System.out.println("File: " + f.getName());
             }
        }
    }
}

Finally, the JSF page uses <o:tree> to reveal the SortedTreeModel:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:o="http://omnifaces.org/ui"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
  <h:head>
    <title></title>               
  </h:head>
  <h:body>    
    <h3>Selected file: <h:outputText id="selectedFile" value="#{treeBean.selected}"/></h3>
    <h:panelGroup id="pathTree">           
      <h:form id="form">
        <o:tree value="#{treeBean.tree}" var="t" varNode="node">
          <o:treeNode>
            <ul type="none">
              <o:treeNodeItem>
                <li>       
                  <h:panelGroup rendered="#{t.type eq 'file' and t.visible}">                                  
                    <h:graphicImage library="default" name="icons/file.png" title="#{t.path}" alt="#{t.path}"/>
                    <h:commandLink value="#{t.name}" action="#{treeBean.selectedFile(t.path)}">
                      <f:ajax execute="@form" render=":selectedFile"/>
                    </h:commandLink>                                       
                    #{t.size}
                  </h:panelGroup>
                  <h:panelGroup rendered="#{t.type eq 'folder' and t.visible}">
                    <h:commandButton image="resources/default/icons/#{t.expanded ? 'folder_open':'folder_close'}.png" 
                                     action="#{treeBean.showHideItems(node)}">
                      <f:ajax execute="@form" render="@form" />
                    </h:commandButton>                                       
                    #{t.name}                                                                
                    <o:treeInsertChildren />
                  </h:panelGroup>                                                                     
                </li>                           
              </o:treeNodeItem>
            </ul>
          </o:treeNode>                   
        </o:tree>
      </h:form>
    </h:panelGroup>           
  </h:body>
</html>

Niciun comentariu:

Trimiteți un comentariu