/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.ims.cp;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dom4j.tree.DefaultElement;
import org.json.JSONException;
import org.olat.core.gui.components.tree.DnDTreeModel;
import org.olat.core.gui.components.tree.GenericTreeModel;
import org.olat.core.gui.components.tree.GenericTreeNode;
import org.olat.core.gui.components.tree.TreeNode;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.ims.cp.objects.CPItem;
import org.olat.ims.cp.objects.CPOrganization;
/**
*
* Description:<br>
* This is the TreeDataModel which is used for the GUI. (Visual Representation
* of the manifest-data-model)
*
* <P>
* Initial Date: 23.07.2008 <br>
*
* @author Sergio Trentini
*/
public class CPTreeDataModel extends GenericTreeModel implements DnDTreeModel {
private static final long serialVersionUID = -6843143820668185636L;
private static final OLog log = Tracing.createLoggerFor(CPTreeDataModel.class);
private final ContentPackage cp;
private Map<String, String> map, reverseMap;
private int nodeCounter;
private final String rootNodeId;
/**
* Constructor
*
* @param orgaIdentifyer The identifier of the organization element of the
* imsmanifest file.
* @param rootNodeId The root node id of the ext-js tree. (We use an md5 hash
* to make sure it doesn't contain any weird characters.)
*/
public CPTreeDataModel(String orgaIdentifyer, String rootNodeId, ContentPackage cp) {
this.cp = cp;
this.rootNodeId = rootNodeId;
nodeCounter = 1;
map = new Hashtable<String, String>();
reverseMap = new Hashtable<String, String>();
map.put(rootNodeId, orgaIdentifyer);
reverseMap.put(orgaIdentifyer, rootNodeId);
update();
}
/**
* Node-ids in the datamodel must be integers This method returns the next
* nodeID (incremental)
*
* @return
*/
private String getNextNodeID() {
return Integer.toString(nodeCounter++);
}
/**
* manifest-node-identifiers are mapped to tree-model-node-id's Returns the
* corresponding identifier for the given nodeID
*
* @param nodeID
* @return
*/
public String getIdentifierForNodeID(String nodeID) {
String identifier = map.get(nodeID);
return identifier;
}
/**
* manifest-node-identifiers are mapped to tree-model-node-id's Returns the
* corresponding identifier for the given nodeID
*
* @param nodeID
* @return
*/
public String getNodeIDForIdentifier(String identifier) {
String nodeId = reverseMap.get(identifier);
if (nodeId == null) {
nodeId = putIdentifierForNodeID(identifier);
}
return nodeId;
}
/**
* gets a new nodeID for the identifier and puts the key/value pair into the
* map Returns the new nodeID
*
* @param identifier
* @return
*/
private String putIdentifierForNodeID(String identifier) {
String nodeId = reverseMap.get(identifier);
if (nodeId == null) {
nodeId = getNextNodeID();
map.put(nodeId, identifier);
reverseMap.put(identifier, nodeId);
}
return nodeId;
}
public void update() {
String realNodeId = getIdentifierForNodeID(rootNodeId);
DefaultElement el = cp.getElementByIdentifier(realNodeId);
if(el instanceof CPOrganization) {
CPOrganization item = (CPOrganization)el;
String rootTitle = cp.getFirstOrganizationInManifest().getTitle();
GenericTreeNode rootNode = new GenericTreeNode(rootNodeId, rootTitle, item);
rootNode.setIconCssClass("o_cp_org");
List<TreeNode> children = getChildrenFor(rootNodeId);
for(TreeNode child:children) {
rootNode.addChild(child);
buildTreeModel(child);
}
setRootNode(rootNode);
}
}
public void buildTreeModel(TreeNode node) {
List<TreeNode> children = getChildrenFor(node.getIdent());
for(TreeNode child:children) {
node.addChild(child);
buildTreeModel(child);
}
}
public List<TreeNode> getChildrenFor(String nodeId) {
List<TreeNode> nodeList = new ArrayList<TreeNode>();
nodeId = getIdentifierForNodeID(nodeId);
DefaultElement el = cp.getElementByIdentifier(nodeId);
if (el == null) {
log.info("element not found (id " + nodeId + ")");
return nodeList;
}
try {
if (el.getName().equals(CPCore.ORGANIZATION)) {
CPOrganization org = (CPOrganization) el;
for (Iterator<CPItem> it = org.getItemIterator(); it.hasNext();) {
CPItem item = it.next();
addItem(nodeList, item);
}
} else if (el.getName().equals(CPCore.ITEM)) {
CPItem pItem = (CPItem) el;
for (Iterator<CPItem> it = pItem.getItemIterator(); it.hasNext();) {
CPItem item = it.next();
addItem(nodeList, item);
}
} else {
// element is not item nor orga -> ergo wrong element
log.info("unknown element while building treemodel for gui (id: " + nodeId + ")");
}
} catch (JSONException e) {
log.error("error while building treemodel");
}
return nodeList;
}
private void addItem(List<TreeNode> nodeList, CPItem item) throws JSONException {
String nId = putIdentifierForNodeID(item.getIdentifier());
GenericTreeNode child = new GenericTreeNode(nId, item.getTitle(), item);
child.setIconCssClass("o_cp_item");
nodeList.add(child);
}
@Override
public boolean isNodeDroppable(TreeNode node) {
return true;
}
@Override
public boolean isNodeDraggable(TreeNode node) {
return !rootNodeId.equals(node.getIdent());
}
/*
@Override
public List<AjaxTreeNode> getChildrenFor(String nodeId) {
CPManagerImpl cpMgm = (CPManagerImpl) CPManager.getInstance();
Vector<AjaxTreeNode> nodeList = new Vector<AjaxTreeNode>();
nodeId = getIdentifierForNodeID(nodeId);
DefaultElement el = cpMgm.getElementByIdentifier(cp, nodeId);
if (el == null) {
log.info("element not found (id " + nodeId + ")");
return nodeList;
}
try {
if (el.getName().equals(CPCore.ORGANIZATION)) {
CPOrganization org = (CPOrganization) el;
for (Iterator<CPItem> it = org.getItemIterator(); it.hasNext();) {
CPItem item = it.next();
addItem(nodeList, item);
}
} else if (el.getName().equals(CPCore.ITEM)) {
CPItem pItem = (CPItem) el;
for (Iterator<CPItem> it = pItem.getItemIterator(); it.hasNext();) {
CPItem item = it.next();
addItem(nodeList, item);
}
} else {
// element is not item nor orga -> ergo wrong element
log.info("unknown element while building treemodel for gui (id: " + nodeId + ")");
}
} catch (JSONException e) {
log.error("error while building treemodel");
}
return nodeList;
}
private void addItem(Vector<AjaxTreeNode> nodeList, CPItem item) throws JSONException {
String nId;
nId = putIdentifierForNodeID(item.getIdentifier());
AjaxTreeNode child = new AjaxTreeNode(nId, item.getTitle());
if (item.getItems().size() == 0) {
// Expand the leaf in order to get rid of the plus sign in front of it.
child.put(AjaxTreeNode.CONF_EXPANDED, true);
}
child.put(AjaxTreeNode.CONF_ICON_CSS_CLASS, "o_cp_item");
nodeList.add(child);
}*/
/**
* Returns the path of the given item in the tree.
* <p>
* Example: /0/12/3
* <p>
* The empty string signifies the root node.
*
* @param identifier The identifier of the page
* @return The path to the node in the tree
*/
public String getPath(String identifier) {
StringBuffer path = new StringBuffer();
DefaultElement elem = cp.getElementByIdentifier(identifier);
if (elem instanceof CPOrganization) {
// Special case. Somehow, the root path should be an empty string.
path.append("");
} else {
addElementToPath(elem, path);
}
return path.toString();
}
/**
* @param identifier
* @param path
* @param slash
* @param elem
*/
private void addElementToPath(DefaultElement elem, StringBuffer path) {
final String slash = "/";
if (elem instanceof CPItem) {
CPItem item = (CPItem) elem;
path.insert(0, slash).insert(1, getNodeIDForIdentifier(item.getIdentifier()));
DefaultElement parent = item.getParentElement();
if (parent != null) addElementToPath(parent, path);
} else if (elem instanceof CPOrganization) {
CPOrganization item = (CPOrganization) elem;
path.insert(0, slash).insert(1, getNodeIDForIdentifier(item.getIdentifier()));
}
}
public void removePath() {
update();
}
}