com.addthis.tutor.tree.TutorTree.java Source code

Java tutorial

Introduction

Here is the source code for com.addthis.tutor.tree.TutorTree.java

Source

/*
 * Licensed 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.addthis.tutor.tree;

import javax.annotation.Syntax;

import java.io.File;
import java.io.IOException;

import java.util.Iterator;

import com.addthis.basis.util.LessFiles;

import com.addthis.bundle.core.Bundle;
import com.addthis.bundle.table.DataTable;
import com.addthis.codec.config.Configs;
import com.addthis.codec.jackson.Jackson;
import com.addthis.codec.json.CodecJSON;
import com.addthis.hydra.data.tree.DataTreeNode;
import com.addthis.hydra.data.tree.ReadTree;
import com.addthis.hydra.data.tree.ReadTreeNode;
import com.addthis.hydra.task.output.tree.TreeMapper;
import com.addthis.maljson.JSONArray;
import com.addthis.maljson.JSONException;
import com.addthis.maljson.JSONObject;

import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * Handles all tree operations for the Tutor such as processing, building, and querying.
 */
public class TutorTree {

    private TreeInput treeInput;
    private TreeMapper mapper;
    private JSONArray treeArray;
    private boolean inserted;
    private ReadTree readTree;
    private DataTable current;
    private File dir;

    /**
     * Constructs a TutorTree with the specified string and treeStructure structure.
     *
     * @param input         - source of data to populate the tree.
     * @param treeStructure - the specified JSON tree structure.
     */
    public TutorTree(String input, @Syntax("HOCON") String treeStructure, File dir) throws Exception {
        mapper = createMapper(treeStructure, dir);
        treeInput = new TreeInput(input, mapper.getFormat(), mapper);
        treeArray = new JSONArray();
        inserted = false;
        current = null;
        this.dir = dir;
        openTree(dir);
        processBundles();
    }

    /**
     * Opens the TreeMapper.
     */
    public void openTree(File dir) {
        LessFiles.deleteDir(this.dir);
        LessFiles.initDirectory(this.dir);
        mapper.open();
    }

    /**
     * Processes the bundles and adds them to the treeIterator.
     */
    public void processBundles() throws Exception {
        while (treeInput.hasNext()) {
            Bundle bundle = treeInput.next();
            mapper.send(bundle);
        }
        mapper.sendComplete();
        readTree = new ReadTree(new File(dir, "data"));
    }

    /**
     *
     */
    public void sendComplete() {
        mapper.sendComplete();
    }

    /**
     * Closes the tree when the user is finished.
     */
    public void closeTree() {
        System.out.println("Closing " + readTree.toString());
        readTree.close();
    }

    /**
     * Returns a string representation of the treeIterator.
     */
    public String toString() {
        System.out.println(readTree.getIterator().next().toString());
        return toString("", readTree.getRootNode().getIterator());
    }

    /**
     * Recursive helper for the toString() method.
     */
    public String toString(String indent, Iterator<DataTreeNode> treeIterator) {
        String treeString = "";
        while (treeIterator.hasNext()) {
            DataTreeNode next = treeIterator.next();
            treeString = treeString + indent + next.getName() + "\n" + toString(indent + "\t", next.getIterator());
        }

        return treeString;
    }

    /**
     * Converts the treeIterator into a JSON object.
     */
    public JSONArray build() {
        return treeArray = build(readTree.getRootNode().getIterator());
    }

    /**
     * Recursive helper for the build() method.
     */
    public JSONArray build(Iterator<DataTreeNode> treeIterator) {
        JSONArray tempTreeArray = new JSONArray();
        while (treeIterator.hasNext()) {
            JSONObject jsonObject = new JSONObject();
            ReadTreeNode next = (ReadTreeNode) treeIterator.next();
            Iterator<DataTreeNode> nextNodes = next.getIterator();
            try {
                if (nextNodes.hasNext()) {
                    if (CodecJSON.encodeJSON(next).has("data")) {
                        jsonObject.put("title", next.getName() + "*");
                    } else {
                        jsonObject.put("title", next.getName());
                    }
                    jsonObject.put("children", build(nextNodes));
                    jsonObject.put("folder", true);
                } else {
                    if (CodecJSON.encodeJSON(next).has("data")) {
                        jsonObject.put("title", next.getName() + "*");
                    } else {
                        jsonObject.put("title", next.getName());
                    }
                    jsonObject.put("children", build(nextNodes));
                    jsonObject.put("folder", false);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            tempTreeArray.put(jsonObject);
        }
        return tempTreeArray;
    }

    /**
     * Builds the tree one node at a time.
     */
    public JSONArray step() {
        return step(treeArray, readTree.getRootNode().getIterator());
    }

    /**
     * step() helper method
     *
     * @param jArray       - JSONArray representing the tree.
     * @param treeIterator - current node iterator.
     */
    public JSONArray step(JSONArray jArray, Iterator<DataTreeNode> treeIterator) {
        inserted = false;
        if (!treeIterator.hasNext()) {
            return new JSONArray();
        }

        while (treeIterator.hasNext() && !inserted) {
            DataTreeNode next = treeIterator.next();
            Iterator<DataTreeNode> nextNodes = next.getIterator();
            JSONObject currentObject = null;
            int i;
            for (i = 0; i < jArray.length(); i++) {
                try {
                    currentObject = jArray.getJSONObject(i);

                    if (currentObject.get("title").equals(next.getName())
                            || currentObject.get("title").equals(next.getName() + "*")) {
                        step(currentObject.getJSONArray("children"), nextNodes);
                        if (treeIterator.hasNext()) {
                            next = treeIterator.next();
                            nextNodes = next.getIterator();
                        }
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }

            Object currentName = new Object();
            if (currentObject != null) {
                try {
                    currentName = currentObject.get("title");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }

            if (i == jArray.length() && !inserted
                    && !(currentName.equals(next.getName()) || currentName.equals(next.getName() + "*"))) {
                JSONObject jsonObject = new JSONObject();
                try {
                    if (CodecJSON.encodeJSON(next).has("data")) {
                        jsonObject.put("title", next.getName() + "*");
                    } else {
                        jsonObject.put("title", next.getName());
                    }
                    jsonObject.put("children", new JSONArray());
                    if (nextNodes.hasNext()) {
                        jsonObject.put("folder", true);
                    } else {
                        jsonObject.put("folder", false);
                    }
                    inserted = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }

                jArray.put(jsonObject);
            }
        }
        return jArray;
    }

    /**
     * Method to query data directory.
     *
     * @param path - specified query path.
     * @param ops  - specified query ops.
     */
    public DataTable query(String path, String ops) throws Exception {
        TreeTutorQueryUtil queryUtil = new TreeTutorQueryUtil();
        current = queryUtil.runQuery(path, ops, dir);
        if (current.size() == 0) {
            int index = path.indexOf(":");
            do {
                String tempPath = path.substring(0, index) + "/+";
                current = queryUtil.runQuery(tempPath, null, dir);
                index = path.indexOf(":", index + 1);
            } while (current.size() != 0 && index < path.length());
        }
        return current;
    }

    /**
     * Checks the query path to check for bad paths that return no matches.
     *
     * @param path
     * @return
     * @throws Exception
     */
    public String checkPath(String path) throws Exception {
        TreeTutorQueryUtil queryUtil = new TreeTutorQueryUtil();
        String temp = path.substring(0, path.indexOf(":") > 0 ? path.indexOf(":") : path.length());
        int index = -1;
        String tempPath;
        do {
            index = temp.indexOf("/", index + 1);
            tempPath = temp.substring(0, index > 0 ? index : temp.length()) + "/+";
            current = queryUtil.runQuery(tempPath, null, dir);
        } while (current.size() != 0 && index > -1);

        tempPath = tempPath.substring(0, tempPath.length() - 2);

        if (current.size() == 0) {
            return "The path '" + tempPath + "' isn't returning any matches. Double check to make sure '"
                    + tempPath.substring(tempPath.lastIndexOf("/") + 1, tempPath.length())
                    + "' is a valid branch or if it " + "actually doesn't have any matches.";
        } else {
            return "The beginning of the path specified in your query appears to be correct, but the rest of your "
                    + "query isn't returning any results. Double check to make sure the rest of your query is correct.";
        }
    }

    /**
     * Returns a JSON representation of the query results.
     */
    public JSONArray getTable() {
        JSONArray dataArray = new JSONArray();

        for (int i = 0; i < current.size(); i++) {
            Bundle bundle = current.get(i);
            JSONArray bundleArray = new JSONArray();
            for (int j = 0; j < bundle.getCount(); j++) {
                bundleArray.put(bundle.getValue(bundle.getFormat().getField(j)).asString());
            }
            dataArray.put(bundleArray);
        }

        return dataArray;
    }

    /**
     * Finds and returns the node specified by the given path.
     *
     * @param path
     * @return
     */
    public DataTreeNode find(String path) {
        DataTreeNode node = readTree.getRootNode();
        String nodeName;

        while (path != null) {
            if (path.indexOf("/") > 0) {
                nodeName = path.substring(0, path.indexOf("/"));
                path = path.substring(path.indexOf("/") + 1, path.length());
            } else {
                nodeName = path.substring(0, path.length());
                path = null;
            }

            node = node.getNode(nodeName);
        }

        return node;
    }

    /**
     * Returns the data associated with the specified title.
     */
    public JSONObject getData(String path) throws Exception {
        ReadTreeNode node = (ReadTreeNode) find(path);
        JSONObject nodeJSON = CodecJSON.encodeJSON(node);
        JSONObject data = null;
        if (nodeJSON.has("data")) {
            data = nodeJSON.getJSONObject("data");
        }
        return data;
    }

    private TreeMapper createMapper(String configuration, File dir) throws IOException {
        ObjectNode treeConfig = Configs.decodeObject(ObjectNode.class, configuration);
        treeConfig.with("config").put("dir", dir.toString());
        return Jackson.defaultCodec().decodeObject(TreeMapper.class, treeConfig);
    }
}