Java Fluent API - better method? - java

Say I have an object that I've created to further simplify reading an XML document using the DOM parser. In order to "step into" a node or element, I'd like to use a single line to go from the start of the document to my target data, buried somewhere within the document, while bypassing the extra "fluff" of the DOM parser (such as doc.getElementsByTagName("data").item(0) when there is only one item inside the "data" element).
For the sake of this question, let's just assume there are no duplicate element tags and I know where I need to navigate to to get the data I need from the document, of which the data is a simple string. The idea is to set the simplified reader up so that it can be used for other data in other locations in the document, as well, without having to write new methods all the time. Below is some example code I've tried:
public class SimplifiedReader {
Document doc;
Element ele;
public SimplifiedReader(Document doc) {
this.doc = doc;
ele = doc.getDocumentElement();
}
public SimplifiedReader fromRoot() {
ele = doc.getDocumentElement();
return this;
}
public SimplifiedReader withEle(String elementName) {
ele = ele.getElementsByTagName(elementName).item(0);
return this;
}
public String getTheData(String elementName) {
return ele.getTextContent();
}
}
Example XML File:
<?xml version="1.0" encoding="UTF-8"?>
<fileData>
<subData>
<targetData>Hello World!</targetData>
<otherData>FooBar!</otherData>
</subData>
</fileData>
This results in me being able to navigate the XML file, and retrieve the Strings "Hello World!" and "FooBar!" using this code:
SimplifiedReader sr = new SimplifiedReader(doc);
String hwData = sr.withEle("fileData").withEle("subData").getTheData("targetData");
String fbData = sr.getTheData("otherData");
Or, if I had to go to another thread to get the data "FooBar!", I would just do:
String fbData = sr.fromRoot().withEle("fileData2").withEle("subData2").getTheData("otherData");
Is there a better/more correct way to do this? Edit: Note: This question is more about the method of returning an object from a method inside of it (return this;) in order to reduce the amount of code written to access specific data stored within a tree format and not so much about how to read an XML file. (I originally thought this was the Singleton Pattern until William corrected me... thank you William).
Thanks in advance for any help.

I don't see any trace of the Singleton pattern here. It mostly resembles the Builder pattern, but isn't it, either. It just implements a fluent API.
Your approach seems very nice and practical.
I would perhaps advise not using fromRoot() but instead constructing a new instance each time. The instance is quite lightweight since all the heavyweight stuff resides in the Document instance it wraps.
You could even go immutable all the way, returning a new instance from withEle(). This buys you many cool properties, like the freedom to share the object around, each code path being free to use it as a starting point to fetch something specific relative to it, share it across threads, etc. The underlying Document is mutable, but usually this doesn't create real-life problems when the code is all about reading.

Is there a better/more correct way to do this?
Yes, there are many better ways to extract values from XML.
One would be to use XPath, for example with XMLBeam.
import java.io.IOException;
import org.xmlbeam.XBProjector;
import org.xmlbeam.annotation.XBDocURL;
import org.xmlbeam.annotation.XBRead;
public class App {
public static void main(String[] args) throws IOException {
FileDate fileDate = new XBProjector().io().fromURLAnnotation(FileDate.class);
System.out.println(fileDate.getTargetDate());
// Hello World!
System.out.println(fileDate.getOtherDate());
// FooBar!
}
#XBDocURL("resource://filedate.xml")
public interface FileDate {
#XBRead("/fileData/subData/targetData")
String getTargetDate();
#XBRead("/fileData/subData/otherData")
String getOtherDate();
}
}

Related

Flatbuffers: how do you build nested tables?

I have a 3-level nested Java POJO that looks like this in the schema file:
struct FPathSegment {
originIata:ushort;
destinationIata:ushort;
}
table FPathConnection {
segments:[FPathSegment];
}
table FPath {
connections:[FPathConnection];
}
When I try to serialize a Java POJO to the Flatbuffer equivalent I pretty much get "nested serialzation is not allowed" error every time I try to use a common FlatBufferBuilder to build this entire object graph.
There is no clue in the docs to state if I have a single builder for the entire graph? A separate one for every table/struct? If separate, how do you import the child objects into the parent?
There are all these methods like create/start/add various vectors, but no explanation what builders go in there. Painfully complicated.
Here is my Java code where I attempt to serialize my Java POJO into Flatbuffers equivalent:
private FPath convert(Path path) {
FlatBufferBuilder bld = new FlatBufferBuilder(1024);
// build the Flatbuffer object
FPath.startFPath(bld);
FPath.startConnectionsVector(bld, path.getConnections().size());
for(Path.PathConnection connection : path.getConnections()) {
FPathConnection.startFPathConnection(bld);
for(Path.PathSegment segment : connection.getSegments()) {
FPathSegment.createFPathSegment(bld,
stringCache.getPointer(segment.getOriginIata()),
stringCache.getPointer(segment.getDestinationIata()));
}
FPathConnection.endFPathConnection(bld);
}
FPath.endFPath(bld);
return FPath.getRootAsFPath(bld.dataBuffer());
}
Every start() method throws a "FlatBuffers: object serialization must not be nested" exception, can't figure out what is the way to do this.
You use a single FlatBufferBuilder, but you must finish serializing children before starting the parents.
In your case, that requires you to move FPath.startFPath to the end, and FPath.startConnectionsVector to just before that. This means you need to store the offsets for each FPathConnection in a temp array.
This will make the nesting error go away.
The reason for this inconvenience is to allow the serialization process to proceed without any temporary data structures.

XPages: Document conflict when saving a doc via SSJS and Java

I have an XPage that is saving a document inside SSJS with document1.save(). After this, I call some Java code to do some additional processing of the document and the new data that was saved; I pass document1.getDocument() in to the Java function. In the Java function, it calls Document.save() to save the document again. This seems to be a recipe for getting a save conflict, and I don't know why. Can anyone explain what's happening? TIA! (In addition to understanding why this is happening, if anyone has suggestions for a better way to do what I'm doing, I'd appreciate it.)
Reid
You can use "resolveVariable" in Java to get hold of your NotesXspDocument (which is called DominoDocument in Java). You can then do your save on the DominoDocument object in Java instead of in SSJS.
If you use JSFUtil (which is found in many XPages open source projects) or use your own helper method, you can then do this to get hold of your DominoDocument (replace "currentDocument" with the name of your document data source):
DominoDocument uidoc = (DominoDocument) JSFUtil.resolveVariable("currentDocument");
The resolveVariable method looks like this:
public static Object resolveVariable(final String variable) {
return FacesContext.getCurrentInstance().getApplication().getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(), variable);
}

What is "Document" keyword and ".something" means?

I have this code:
Public static List <LinkNode> parse (LinkNode inputLink) {
List <LinkNode> outputLinks = new LinkList<>();
try {
Document parsedResults = Jsoup
.connect (inputLink.getUrl ())
.timeout (READ_TIMEOUT_IN_MILLISSECS)
.get ();
}
String tag;
Elements elements;
List <LinkNode> result;
}
I have few questions about the code:
Is Document a reserved keyword Java?
try {
Document parsedResults = Jsoup
.connect (inputLink.getUrl ())
.timeout (READ_TIMEOUT_IN_MILLISSECS)
.get ();
What is this form of coding (dot something)? Can I write it in another form If yes can you show me how?
.connect (inputLink.getUrl ())
.timeout (READ_TIMEOUT_IN_MILLISSECS)
.get ();
No, it is not a reserved keyword. In this case, Document is the name of a class defined in the Jsoup library. Other libraries can have their own class named Document, or you could write your own. For example:
In contrast, reserved keywords cannot be used as identifiers (i.e. class names, function names, variable names). These words include class, try, if, while, public, private, and more. See a bigger list here.
The dot (.) operator allows you to access an object's functions or variables. For example rectangle.width or dog.run().
Your example shows method chaining, in which subsequent methods are called on the object returned by the previous method. This removes the need for creating intermediate variables and makes things easier (for you as a programmer and also someone that is reading your code).
You could achieve exactly the same result with more code:
Connection conn1 = Jsoup.connect(inputLink.getUrl ());
Connection conn2 = conn1.timeout(READ_TIMEOUT_IN_MILLISSECS);
Document parsedResults = conn2.get();
1) No, it's a class defined in the Jsoup library.
2) It's part of a normal method call, as in Integer.parseInt("5") or scanner.readLine().

Two Threads calling one Method

I have a method which writes a XML- File like this:
private void doProcess() {
Element rootElement = mDoc.createElement("Test");
mDoc.appendChild(rootElement);
....... I build the whole document here...
}
But this method can be called by multiple Threads, so for example if thwo threads call this method on the same time i get an
): org.w3c.dom.DOMException: Only one root element allowed
I already tried it with an reentrantlock, but this didn´t work...Can somebody give me a hint?
Edit:
I dont build the Document with multiple Threads...Every Call of my Method builds his own Doc...So sometimes in my Application it could happen that my Method will be called two times at the same time...And there´s my Problem...
In the question you state:
I dont build the Document with multiple Threads...Every Call of my Method builds his own Doc
Currently the code given shares a single doc between all calls to the function. In order to have each call to the function work on it's own document, you need to modify the code such that each call has it's own doc.
This can be done either by creating and returning a new document object
private XMLDocument doProcess() {
XMLDocument mDoc = new XMLDocument(); // or simmilar depending on XML library
Element rootElement = mDoc.createElement("Test");
mDoc.appendChild(rootElement);
// ....... I build the whole document here...
return mDoc; //return the document object
}
Or, by passing the document object in as a parameter
private void doProcess(XMLDocument mDoc) { ... }
An xml can has only ONE root, so this may be answer to your question. You can instantiate an root element outside this method and add element to this root inside method each time.

Get declared methods in order they appear in source code

The situation seems to be abnormal, but I was asked to build serializer that will parse an object into string by concatenating results of "get" methods. The values should appear in the same order as their "get" equivalent is declared in source code file.
So, for example, we have
Class testBean1{
public String getValue1(){
return "value1";
}
public String getValue2(){
return "value2";
}
}
The result should be:
"value1 - value2"
An not
"value2 - value1"
It can't be done with Class object according to the documentation. But I wonder if I can find this information in "*.class" file or is it lost? If such data exists, maybe, someone knows a ready to use tool for that purpose? If such information can't be found, please, suggest the most professional way of achieving the goal. I thought about adding some kind of custom annotations to the getters of the class that should be serialized.
If you want that you have to parse the source code, not the byte code.
There are a number of libraries that parse a source file into a node tree, my favorite is the javaparser (hosted at code.google.com), which, in a slightly modified version, is also used by spring roo.
On the usage page you can find some samples. Basically you will want to use a Visitor that listens for MethodDefinitions.
Although reflection does not anymore (as of java 7 I think) give you the methods in the order in which they appear in the source code, the class file appears to still (as of Java 8) contain the methods in the order in which they appear in the source code.
So, you can parse the class file looking for method names and then sort the methods based on the file offset in which each method was found.
If you want to do it in a less hacky way you can use Javassist, which will give you the line number of each declared method, so you can sort methods by line number.
I don't think the information is retained.
JAXB, for example, has #XmlType(propOrder="field1, field2") where you define the order of the fields when they are serialized to xml. You can implemenet something similar
Edit: This works only on concrete classes (the class to inspect has its own .class file). I changed the code below to reflect this. Until diving deeper into the ClassFileAnalyzer library to work with classes directly instead of reading them from a temporary file this limitation exists.
Following approach works for me:
Download and import following libarary ClassFileAnalyzer
Add the following two static methods (Attention! getClussDump() needs a little modification for writing out the class file to a temporary file: I removed my code here because it's very special at this point):
public static String getClassDump(Class<?> c) throws Exception {
String classFileName = c.getSimpleName() + ".class";
URL resource = c.getResource(classFileName);
if (resource == null) {
throw new RuntimeException("Works only for concreate classes!");
}
String absolutePath = ...; // write to temp file and get absolute path
ClassFile classFile = new ClassFile(absolutePath);
classFile.parse();
Info infos = new Info(classFile, absolutePath);
StringBuffer infoBuffer = infos.getInfos();
return infoBuffer.toString();
}
public static <S extends List<Method>> S sortMethodsBySourceOrder(Class<?> c, S methods) throws Exception {
String classDump = getClassDump(c);
int index = classDump.indexOf("constant_pool_count:");
final String dump = classDump.substring(index);
Collections.sort(methods, new Comparator<Method>() {
public int compare(Method o1, Method o2) {
Integer i1 = Integer.valueOf(dump.indexOf(" " + o1.getName() + lineSeparator));
Integer i2 = Integer.valueOf(dump.indexOf(" " + o2.getName() + lineSeparator));
return i1.compareTo(i2);
}});
return methods;
}
Now you can call the sortMethodsBySourceOrder with any List of methods (because sorting arrays is not very comfortable) and you will get the list back sorted.
It works by looking at the class dumps constant pool which in turn can be determined by the library.
Greetz,
GHad
Write your custom annotation to store ordering data, then use Method.getAnnotation(Class annotationClass)

Categories