Poking around the iNotes HTTP API (Part 2) - Fun with Rhino
The iNotes HTTP API wasn't designed for consumption outside the iNotes web client. This becomes apparent when looking at
To make use of data included in the JavaScript there are 2 possibilities:
Turning the outline information into something Java useful we need to parse the returned JSON, lets see how that works. The Outline is a little more complex that it might seem. The JSON delivers 6 values (in an array) about the outline entries:
Then the code turns out quite lean:
The String
Instead of
Now the challenge: provide a faster way to generate a collection of
Form=l_GetOutline_JSON
or Form=l_JSVars
that return JavaScript and not JSON! The difference? JSON contains data only, while JavaScript contains function
definitions too.
To make use of data included in the JavaScript there are 2 possibilities:
- do some elaborate String operation
- actually run JavaScript
JSON.stringify
or JSON.parse
are missing and need to be added from elsewhere.
Turning the outline information into something Java useful we need to parse the returned JSON, lets see how that works. The Outline is a little more complex that it might seem. The JSON delivers 6 values (in an array) about the outline entries:
- The hierarchical sequence number. Something like 1 or 3.1 or 3.2.1 - depending on the level. The numbers have gaps where entries in the Outline are hidden from the web
- The hierarchy level starting with 0. So Asia\South-East\Singapore would be level 2
- The Label of the entry. Can be different from the actual name of the element
- The full path to the image for that entry. We'll strip that to get the name without a path, so we can use our own icons
- The full URL to that entry. It has the View Label (Name) and its UNID or system name in the string. We need the UNID for the unread count, so we'll extract that
- Can something be dragged there. 1 = Yes, it is a real folder, 0 = probably not, it is a view or a label only. "Probably" since e.g. you should be able to drag into the "Delete" view
Then the code turns out quite lean:
public OutlineInfo getOutlineInfo(String code, String outline) {
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");
engine.eval(code);
Invocable jsFunc = (Invocable) engine;
OutlineInfo result = (OutlineInfo) jsFunc.invokeFunction("getOutlineObject", outline);
return result;
}
The String
code
contains our JavaScript function. In a working system I probably would keep the engine around somewhere to save the spinup time. outline
contains what came back from the call to iNotes. The JavaScript code is equally lean:
function getOutlineObject(raw) {
importPackage(com.notessensei.demo);
var arr;
eval("arr = "+raw);
var result = new OutlineInfo();
for (var line in arr.outline) {
var oe = new OutlineEntry(arr.outline[line][0], arr.outline[line][1],
arr.outline[line][2], arr.outline[line][3],
arr.outline[line][4], arr.outline[line][5]);
result.addEntry(oe);
}
return result;
}
Instead of
OutlineInfo
you could use a Java collection to return the value, so I won't list the class here. The OutlineEntry
is more interesting, so you can run the sample yourself:
package com.notessensei.demo;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
public class OutlineEntry {
public static final String DEFAULT_ICON = "outlineentry.gif";
private final String id;
private final int level;
private final String label;
private final String icon;
private final String unid;
private final String url;
private final boolean isDropTarget;
private int unreadCount = 0;
// TODO: do I want it rather by sequence, then a TreeSet would be nice
private final Set<OutlineEntry> children = new LinkedHashSet<OutlineEntry>();
public OutlineEntry(String id, int level) {
this.id = id;
this.level = level;
this.label = ">"+id;
this.icon = DEFAULT_ICON;
this.unid = null;
this.url = null;
this.isDropTarget = false;
}
public OutlineEntry(String id, int level, String label, String iconFullPath, String fullURL, int draggable) {
this.id = id;
this.level = level;
this.label = label;
this.icon = this.extractIcon(iconFullPath);
this.unid = this.extractUNID(fullURL);
this.url = fullURL;
this.isDropTarget = (draggable > 0);
}
private String extractUNID(String fullURL) {
if (fullURL==null || fullURL.equals("") || fullURL.indexOf("s_ViewName;")<0) {
return null;
}
String workString = fullURL.substring(fullURL.indexOf("s_ViewName;")+11)+",";
return workString.substring(0,workString.indexOf(","));
}
private String extractIcon(String iconFullPath) {
if (iconFullPath == null || iconFullPath.equals("")) {
return null;
}
String workString = iconFullPath + "/?OpenImageResource";
String[] split = workString.substring(0,workString.indexOf("/?OpenImageResource")).split("/");
return split[split.length-1];
}
public void addChild(OutlineEntry entry) {
this.children.add(entry);
}
public void addChildren(Collection<OutlineEntry> children) {
this.children.addAll(children);
}
public Collection<OutlineEntry> getChildren() {
return this.children;
}
public String getIcon() {
return this.icon;
}
public String getId() {
return this.id;
}
public String getLabel() {
return this.label;
}
public int getLevel() {
return this.level;
}
public String getParent() {
if (this.level == 0) {
return null;
}
return this.id.substring(0, this.id.lastIndexOf("."));
}
public String getUNID() {
return this.unid;
}
public int getUnreadCount() {
return this.unreadCount;
}
public String getUrl() {
return this.url;
}
public boolean isDropTarget() {
return this.isDropTarget;
}
public void setUnreadCount(int unreadCount) {
this.unreadCount = unreadCount;
}
}
Now the challenge: provide a faster way to generate a collection of
OutlineEntry
objects! The code above takes on my system between 100 and 150msPosted by Stephan H Wissel on 27 November 2014 | Comments (0) | categories: IBM Notes