/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.esri.gpt.control.search.browse;
import com.esri.gpt.framework.context.ConfigurationException;
import com.esri.gpt.framework.util.Val;
import com.esri.gpt.framework.xml.DomUtil;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Represents a table of contents tree.
*/
public class TocTree {
/** instance variables ====================================================== */
private List<TocItem> children = new ArrayList<TocItem>();
private String idProperty;
private String labelProperty;
/** constructors ============================================================ */
/** Default constructor */
public TocTree() {}
/** properties ============================================================== */
/**
* Gets the children.
* @return the children
*/
public List<TocItem> getChildren() {
return this.children;
}
/**
* Sets the children.
* @param children the children
*/
public void setChildren(List<TocItem> children) {
this.children = children;
}
/**
* Gets the ID property.
* @return the ID property
*/
public String getIDProperty() {
return this.idProperty;
}
/**
* Sets the ID property.
* @param attribute the ID property
*/
public void setIDProperty(String attribute) {
this.idProperty = attribute;
}
/**
* Gets the label property.
* @return the label property
*/
public String getLabelProperty() {
return this.labelProperty;
}
/**
* Sets the label property.
* @param attribute the label property
*/
public void setLabelProperty(String attribute) {
this.labelProperty = attribute;
}
/** methods ================================================================= */
/**
* Returns a JSON representation of the property.
* @return the JSON string
* @throws IOException if an exception occurs
*/
public String asJson() throws IOException {
PrintWriter pw = null;
try {
StringWriter sw = new StringWriter();
pw = new PrintWriter(sw);
this.toJson(pw,0,true);
pw.flush();
return sw.toString();
} finally {
try {if (pw != null) pw.close();} catch (Exception ef) {}
}
}
/**
* Returns an XML representation of the property.
* @return the XML string
* @throws IOException if an exception occurs
*/
public String asXml() throws IOException {
PrintWriter pw = null;
try {
StringWriter sw = new StringWriter();
pw = new PrintWriter(sw);
pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
this.toXml(pw,0);
pw.flush();
return sw.toString();
} finally {
try {if (pw != null) pw.close();} catch (Exception ef) {}
}
}
/**
* Builds a tree.
* @param context the operation context
* @param relativePath path the associated XML document
* @return the tree
*/
public static TocTree build(TocContext context, String relativePath) throws Exception {
// create the DOM determine the root node
Node root = null;
Document dom = DomUtil.makeDomFromResourcePath(relativePath,false);
NodeList nl = dom.getChildNodes();
for (int i=0; i<nl.getLength(); i++) {
Node ndChild = nl.item(i);
if (ndChild.getNodeType() == Node.ELEMENT_NODE) {
String nodeName = Val.chkStr(ndChild.getNodeName());
if (nodeName.equalsIgnoreCase("tree")) {
root = ndChild;
}
break;
}
}
// process the tree node
if (root != null) {
TocTree tree = null;
Node ndClassName = root.getAttributes().getNamedItem("className");
if (ndClassName == null) {
tree = new TocTree();
} else {
tree = TocTree.makeTreeInstance(context,ndClassName.getNodeValue());
}
tree.processTreeNode(context,root);
return tree;
} else {
String msg = "Unable to locate <tree> root node within: "+relativePath;
throw new ConfigurationException(msg);
}
}
/**
* Generates the response string.
* @param context the operation context
* @return the response string
* @throws Exception if an exception occurs
*/
public String generateResponse(TocContext context) throws Exception {
String fmt = Val.chkStr(context.getOutputFormat());
boolean isJson = fmt.equalsIgnoreCase("") ||
fmt.equalsIgnoreCase("json") ||
fmt.equalsIgnoreCase("pjson") ||
fmt.equalsIgnoreCase("application/json");
if (isJson) {
return this.asJson();
} else {
return this.asXml();
}
}
/**
* Determines whether or not this property has children.
* @return <code>true</code> if this property has children
*/
public boolean hasChildren() {
return (this.getChildren() != null) && (this.getChildren().size() > 0);
}
/**
* Makes a TocTree instance based upon a class name.
* @param context the operation context
* @param className the fully qualified class name
* @return the instance
* @throws ClassNotFoundException if the class was not found
* @throws InstantiationException if the class could not be instantiated
* @throws IllegalAccessException if the class could not be accessed
*/
public static TocTree makeTreeInstance(TocContext context, String className)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
className = Val.chkStr(className);
if (className.length() == 0) {
return new TocTree();
} else {
Class<?> cls = Class.forName(className);
Object obj = cls.newInstance();
if (obj instanceof TocTree) {
return (TocTree)obj;
} else {
String msg = "The configured tree.className is invalid: "+className;
throw new ConfigurationException(msg);
}
}
}
/**
* Processes a TOC tree node.
* @param context the operation context
* @param node the tree node
* @throws Exception if an exception occurs
*/
public void processTreeNode(TocContext context, Node node) throws Exception {
NodeList nlChildren = node.getChildNodes();
for (int i=0; i<nlChildren.getLength(); i++) {
Node ndChild = nlChildren.item(i);
if (ndChild.getNodeType() == Node.ELEMENT_NODE) {
String nodeName = Val.chkStr(ndChild.getNodeName());
if (nodeName.equalsIgnoreCase("identifier")) {
this.setIDProperty(ndChild.getTextContent());
} else if (nodeName.equalsIgnoreCase("label")) {
this.setLabelProperty(ndChild.getTextContent());
} else if (nodeName.equalsIgnoreCase("item")) {
TocItem subItem = null;
Node ndClassName = ndChild.getAttributes().getNamedItem("className");
if (ndClassName == null) {
subItem = new TocItem();
} else {
subItem = TocItem.makeItemInstance(context,ndClassName.getNodeValue());
}
subItem.processItemNode(context,ndChild);
if (subItem.isValid()) {
this.getChildren().add(subItem);
}
}
}
}
}
/**
* Writes a JSON representation of the property.
* @param writer the writer
* @param depth the depth of the parent
* @throws IOException if an exception occurs
*/
public void toJson(PrintWriter writer, int depth, boolean isLast) throws IOException {
String pfx = "";
for (int i=0;i<2*depth;i++) pfx += " ";
String pfx2 = pfx+" ";
String line;
boolean hc = this.hasChildren();
String sIdProp = Val.chkStr(this.getIDProperty());
String sLabelProp = Val.chkStr(this.getLabelProperty());
writer.println(pfx+"{");
if (sIdProp.length() > 0) {
line = pfx2+"\"identifier\": \""+Val.escapeStrForJson(sIdProp)+"\"";
if (hc || (sLabelProp.length() > 0)) line +=",";
writer.println(line);
}
if (sLabelProp.length() > 0) {
line = pfx2+"\"label\": \""+Val.escapeStrForJson(sLabelProp)+"\"";
if (hc) line +=",";
writer.println(line);
}
if (hc) {
line = pfx2+"\"items\": [";
writer.println(line);
for (int i=0; i<this.getChildren().size(); i++) {
TocItem child = this.getChildren().get(i);
boolean bLast = (i >= (this.getChildren().size() - 1));
child.toJson(writer,(depth+2),bLast);
}
writer.println(pfx2+"]");
}
line = pfx+"}";
if (!isLast) line +=",";
writer.println(line);
}
/**
* Writes an XML representation of the property.
* @param writer the writer
* @param depth the depth of the parent
* @throws IOException if an exception occurs
*/
public void toXml(PrintWriter writer, int depth) throws IOException {
String pfx = "";
for (int i=0;i<2*depth;i++) pfx += " ";
String pfx2 = pfx+" ";
String sIdProp = Val.chkStr(this.getIDProperty());
String sLabelProp = Val.chkStr(this.getLabelProperty());
writer.println(pfx+"<tree>");
if (sIdProp.length() > 0) {
writer.println(pfx2+"<identifier>"+Val.escapeXml(sIdProp)+"</identifier>");
}
if (sLabelProp.length() > 0) {
writer.println(pfx2+"<label>"+Val.escapeXml(sLabelProp)+"</label>");
}
if (this.hasChildren()) {
for (TocItem child: this.getChildren()) {
child.toXml(writer,(depth+1));
}
}
writer.println(pfx+"</tree>");
}
}