9.5. A Few Notes on Defining New Contexts
Adding a new context to JXPath can involve a significant amount of work, as can readily be seen from the preceding section. Fortunately, there are often approaches that will simplify the process.First, it may be possible to extend or modify an existing context. In this case, it might have been reasonable to start with BeanPointer and related classes that handle bean-based contexts. Once again, having access to the source code can be an invaluable aid in extending the system.Second, it may be possible to recast the problem to reuse an existing context. When considering this approach the first question to ask is whether the object in question acts like a bean. If so, then the existing JXPath bean handler can be used, and no new code needs to be written. In this case, File does not act enough like a bean for this to be done. The primitive values like name and modification time do behave like bean properties, but the subdirectories and files within a directory can only be obtained as an entire array, not as an indexed property.Fortunately, there is an easy fix; it would be possible to create a bean that wraps a File object. Such a bean for directories is shown in Listing 9.11.
Listing 9.11. A file bean
Note that the subdirectories and files are not obtained until they are needed. If buildArrays() were called in the constructor, the entire directory tree would be read into memory when the root node was constructed, which could be very wasteful if it turned out that only a small part were needed.The corresponding FileBean is similar, except it omits the getFile() and getdirectory() methods and adds getContents().Much of the problem-specific code that appeared in the custom file system context also appears in these beans, although in slightly different forms. In particular, this is the code that gets primitive values like the name, along with code that traverses a directory and gets contents from a file. What is missing is all the code that deals with the intricacies of the JXPath system. As such, wrapping the problem in a bean and using existing JXPath tools is clearly an easier solution and should be used whenever possible.
package com.awl.toolbook.chapter09;
import java.io.File;
import java.util.ArrayList;
public class DirectoryBean {
public DirectoryBean() {}
public DirectoryBean(File f) {
setTheFile(f);
}
private File theFile;
public File getTheFile() {return theFile;}
public void setTheFile(File theFile) {
this.theFile = theFile;
}
public String getName() {return theFile.getName();}
public void setName(String name) {}
public long getModifiedTime() {
return theFile.lastModified();
}
public void setModifiedTime(long modifiedTime) {}
private boolean arraysBuilt = false;
private void buildArrays() {
if(arraysBuilt) return;
File children[] = theFile.listFiles();
subdirs = new ArrayList();
files = new ArrayList();
for(int i=0;i<children.length;i++) {
if(children[i].isDirectory()) {
subdirs.add(new DirectoryBean(children[i]));
} else {
files.add(new FileBean(children[i]));
}
}
}
private ArrayList subdirs;
public File getDirectory(int i) {
buildArrays();
return (File) subdirs.get(i);
}
public void setDirectory(int i,File f) {}
private ArrayList files;
public File getFile(int i) {
buildArrays();
return (File) files.get(i);
}
public void setFile(int i,File f) {}
}