/**********************************************************************************
* nWordPress is an automated migration of WordPress 2.5.1 performed by Numiton.
*
* copyright : (C) 2008 Numiton - www.numiton.com
* email : numiton@users.sourceforge.net
*
* $Id: Walker.java,v 1.4 2008/10/14 13:15:49 numiton Exp $
*
**********************************************************************************/
/**********************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
**********************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
package org.numiton.nwp.wp_includes;
import static com.numiton.VarHandling.*;
import java.io.Serializable;
import java.util.Map;
import org.apache.log4j.Logger;
import org.numiton.nwp.GlobalConsts;
import org.numiton.nwp.GlobalVars;
import com.numiton.FunctionHandling;
import com.numiton.array.Array;
import com.numiton.array.ArrayEntry;
import com.numiton.generic.*;
/*
* A class for displaying various tree-like structures.
* Extend the Walker class to use it, see examples at the bottom
*/
public abstract class Walker implements ContextCarrierInterface, Serializable, Cloneable {
protected static final Logger LOG = Logger.getLogger(Walker.class.getName());
public GlobalConsts gConsts;
public GlobalVars gVars;
public String tree_type;
public Array<Object> db_fields = new Array<Object>();
public Walker(GlobalVars javaGlobalVariables, GlobalConsts javaGlobalConstants) {
setContext(javaGlobalVariables, javaGlobalConstants);
}
/**
* abstract callbacks
*/
public abstract void start_lvl(Ref<String> output, int depth, Array<Object> args);
public abstract void end_lvl(Ref<String> output, int depth, Array<Object> args);
public abstract void start_el(Ref<String> output, StdClass page, int depth, Array<Object> args);
public abstract void end_el(Ref<String> output, StdClass page, int depth, Array<Object> deprecated);
/**
* display one element if the element doesn't have any children
* otherwise, display the element and its children
*/
public void display_element(StdClass element, Array<Object> children_elements, int max_depth, int depth, Array<Object> args, Ref<String> output) {
String id_field = null;
String parent_field = null;
Array<Object> cb_args = new Array<Object>();
StdClass child = null;
int i = 0;
Boolean newlevel = null;
if (!booleanval(element)) {
return;
}
id_field = strval(this.db_fields.getValue("id"));
parent_field = strval(this.db_fields.getValue("parent"));
//display this element
cb_args = Array.array_merge(new Array<Object>(new ArrayEntry<Object>(output), new ArrayEntry<Object>(element), new ArrayEntry<Object>(depth)), args);
FunctionHandling.call_user_func_array(new Callback("start_el", this), cb_args);
if (equal(max_depth, 0) || (!equal(max_depth, 0) && (max_depth > (depth + 1)))) { //whether to descend
for (i = 0; i < Array.sizeof(children_elements); i++) {
child = (StdClass) children_elements.getValue(i);
if (equal(StdClass.getValue(child, parent_field), StdClass.getValue(element, id_field))) {
if (!isset(newlevel)) {
newlevel = true;
//start the child delimiter
cb_args = Array.array_merge(new Array<Object>(new ArrayEntry<Object>(output), new ArrayEntry<Object>(depth)), args);
FunctionHandling.call_user_func_array(new Callback("start_lvl", this), cb_args);
}
Array.array_splice(children_elements, i, 1);
this.display_element(child, children_elements, max_depth, depth + 1, args, output);
i = -1;
}
}
}
if (isset(newlevel) && newlevel) {
//end the child delimiter
cb_args = Array.array_merge(new Array<Object>(new ArrayEntry<Object>(output), new ArrayEntry<Object>(depth)), args);
FunctionHandling.call_user_func_array(new Callback("end_lvl", this), cb_args);
}
//end this element
cb_args = Array.array_merge(new Array<Object>(new ArrayEntry<Object>(output), new ArrayEntry<Object>(element), new ArrayEntry<Object>(depth)), args);
FunctionHandling.call_user_func_array(new Callback("end_el", this), cb_args);
}
/*
* displays array of elements hierarchically
* it is a generic function which does not assume any existing order of elements
* max_depth = -1 means flatly display every element
* max_depth = 0 means display all levels
* max_depth > 0 specifies the number of display levels.
*/
public String walk(Array<Object> elements, int max_depth, Object... vargs) {
Array<Object> args = new Array<Object>();
Ref<String> output = new Ref<String>();
String id_field = null;
String parent_field = null;
Array<Object> empty_array = new Array<Object>();
StdClass e = null;
Array<Object> top_level_elements = new Array<Object>();
Array<Object> children_elements = new Array<Object>();
StdClass root = null;
StdClass child = null;
int i = 0;
StdClass orphan_e = null;
// Modified by Numiton
args = FunctionHandling.func_get_args(vargs);
output.value = "";
if (max_depth < -1) { //invalid parameter
return output.value;
}
if (empty(elements)) { //nothing to walk
return output.value;
}
id_field = strval(this.db_fields.getValue("id"));
parent_field = strval(this.db_fields.getValue("parent"));
// flat display
if (equal(-1, max_depth)) {
empty_array = new Array<Object>();
for (Map.Entry javaEntry416 : elements.entrySet()) {
e = (StdClass) javaEntry416.getValue();
this.display_element(e, empty_array, 1, 0, args, output);
}
return output.value;
}
/*
* need to display in hierarchical order
* splice elements into two buckets: those without parent and those with parent
*/
top_level_elements = new Array<Object>();
children_elements = new Array<Object>();
for (Map.Entry javaEntry417 : elements.entrySet()) {
e = (StdClass) javaEntry417.getValue();
if (equal(0, StdClass.getValue(e, parent_field))) {
top_level_elements.putValue(e);
} else {
children_elements.putValue(e);
}
}
/*
* none of the elements is top level
* the first one must be root of the sub elements
*/
if (!booleanval(top_level_elements)) {
root = (StdClass) children_elements.getValue(0);
for (i = 0; i < Array.sizeof(children_elements); i++) {
child = (StdClass) children_elements.getValue(i);
if (equal(StdClass.getValue(root, parent_field), StdClass.getValue(child, parent_field))) {
top_level_elements.putValue(child);
Array.array_splice(children_elements, i, 1);
i--;
}
}
}
for (Map.Entry javaEntry418 : top_level_elements.entrySet()) {
e = (StdClass) javaEntry418.getValue();
this.display_element(e, children_elements, max_depth, 0, args, output);
}
/*
* if we are displaying all levels, and remaining children_elements is not empty,
* then we got orphans, which should be displayed regardless
*/
if (equal(max_depth, 0) && (Array.sizeof(children_elements) > 0)) {
empty_array = new Array<Object>();
for (Map.Entry javaEntry419 : children_elements.entrySet()) {
orphan_e = (StdClass) javaEntry419.getValue();
this.display_element(orphan_e, empty_array, 1, 0, args, output);
}
}
return output.value;
}
public void setContext(GlobalVariablesContainer javaGlobalVariables, GlobalConstantsInterface javaGlobalConstants) {
gConsts = (GlobalConsts) javaGlobalConstants;
gVars = (GlobalVars) javaGlobalVariables;
gVars.gConsts = gConsts;
}
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public GlobalVariablesContainer getGlobalVars() {
return gVars;
}
}