/**
* Copyright 2008-2016 Qualogy Solutions B.V.
*
* 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.qualogy.qafe.gwt.client.component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.UIObject;
import com.qualogy.qafe.gwt.client.ui.renderer.RendererHelper;
import com.qualogy.qafe.gwt.client.vo.functions.DataContainerGVO;
import com.qualogy.qafe.gwt.client.vo.ui.TreeItemGVO;
public class QTree extends Tree implements HasData {
public static final String NODE = "id";
public static final String NODE_TYPE = "fqn";
public static final String PARENT_NODE = "parent";
public static final String KEY_DELIMITER = "|";
private class TreeHelper implements SelectionHandler<TreeItem> {
public void onSelection(SelectionEvent<TreeItem> event) {
doItemSelection(event);
}
}
private Map<String,Object> items = new HashMap<String, Object>();
private Map<String,String> key2Parent = new LinkedHashMap<String,String>();
private Map<UIObject,Object> uiToKey = new HashMap<UIObject,Object>();
private Map<Object,UIObject> keyToUI = new HashMap<Object, UIObject>();
private Map<Object,Object> keyToUIModel = new HashMap<Object, Object>();
private TreeHelper helper = new TreeHelper();
private TreeItem selectedItem = null;
private boolean structuredData;
private TreeItem rootItem;
private boolean showRootItem = true;
public QTree(TreeItem rootItem) {
this(rootItem, true);
}
public QTree(TreeItem rootItem, boolean showRootItem) {
this.rootItem = rootItem;
this.showRootItem = showRootItem;
if (showRootItem) {
super.addItem(rootItem);
}
addSelectionHandler(helper);
}
@Override
public void addItem(TreeItem item) {
addItem(item, null);
}
public void addItem(TreeItem item, TreeItem parent) {
if (parent != null) {
parent.addItem(item);
} else {
if (showRootItem) {
getItem(0).addItem(item);
} else {
super.addItem(item);
}
}
}
public Object getData() {
List data = null;
if (selectedItem != null) {
Object key = uiToKey.get(selectedItem);
if ((key != null) || ((key == null) && isRootItem(selectedItem))) {
data = new ArrayList();
if (key != null) {
Object uiModel = keyToUIModel.get(key);
data.add(uiModel);
}
List itemList = null;
populateData(data, key, structuredData);
}
}
return data;
}
private boolean isRootItem(TreeItem treeItem) {
return (rootItem == treeItem);
}
private void populateData(List data, Object key, boolean structured) {
Object uiModel;
Iterator<String> itrKey = key2Parent.keySet().iterator();
while (itrKey.hasNext()) {
String nodeKey = itrKey.next();
String parentKey = key2Parent.get(nodeKey);
if ((parentKey == key) || ((parentKey != null) && parentKey.equals(key))) {
uiModel = keyToUIModel.get(nodeKey);
data.add(uiModel);
// in case of structured data the model itself contains the child items in it ,
// so we have child items and relation implicitly.
if ( !structured ) {
populateData(data, nodeKey, structured);
}
}
}
}
public void setData(Object data, String action, Object mapping) {
List itemList = null;
if (data instanceof Map) {
itemList = new ArrayList();
itemList.add(data);
} else if (data instanceof List) {
itemList = (List)data;
}
if (isSetAction(action)) {
clearAll();
}
processItems(itemList, action, mapping);
}
private void clearAll() {
keyToUI.clear();
keyToUIModel.clear();
key2Parent.clear();
uiToKey.clear();
clearItems();
}
private void clearItems() {
if (showRootItem) {
TreeItem rootItem = getItem(0);
if (rootItem != null) {
int childCount = rootItem.getChildCount();
for (int i=childCount-1; i>=0; i--) {
rootItem.getChild(i).remove();
}
}
} else {
int childCount = this.getItemCount();
for (int i=childCount-1; i>=0; i--) {
this.getItem(i).remove();
}
}
}
private void processItems(List itemList, String action, Object mapping) {
createItems(itemList, action, mapping);
connectItems();
}
private void createItems(List itemList, String action, Object mapping) {
if(itemList == null) {
return;
}
for (Object item : itemList) {
Object itemKey = getKeyValue(item, NODE, mapping);
if (itemKey != null) {
items.put(itemKey.toString(), item);
}
}
if (hasStructuredRelation(itemList)) {
structuredData = true;
createItemsFromStructuredData(null, itemList, action, mapping);
} else {
structuredData = false;
createItemsFromKeyData(itemList, action, mapping);
}
}
/**
* In sructured data - from java servive the relation between the items are implicit by its structure.
* We have store the relation for intenal use by iterating the items properties.
*
*/
private void createItemsFromStructuredData(String parentKey, List itemList, String action, Object mapping) {
if (itemList != null) {
for (Object item : itemList) {
String nodeKey = generateKey4Map(item, mapping);
if (nodeKey != null) {
if (parentKey != null) {
nodeKey += KEY_DELIMITER + parentKey;
}
TreeItem treeItem = createTreeItem(item, mapping);
storeUIAndModel(nodeKey, treeItem, item);
key2Parent.put(nodeKey, parentKey);
if (isMapContainer(item)) {
//Map map = (Map)item;
Map map = ((DataContainerGVO)item).getDataMap();
Iterator<String> itrKeys = map.keySet().iterator();
while (itrKeys.hasNext()) {
String key = itrKeys.next();
//Object value = getKeyValue(map, key);
Object value = map.get(key);
if (value instanceof DataContainerGVO) {
DataContainerGVO valueGVO = (DataContainerGVO)value;
if (valueGVO.getKind() == DataContainerGVO.KIND_COLLECTION) {
TreeItem subTreeItem = createTreeItem(key);
String subNodeKey = key + KEY_DELIMITER + nodeKey;
storeUIAndModel(subNodeKey, subTreeItem, null);
createItemsFromStructuredData(subNodeKey, valueGVO.getListofDC(), action, mapping);
key2Parent.put(subNodeKey, nodeKey);
}
}
}
}
}
}
}
}
// If the data contains id and parent as attributes or if user specified in mapping the values for it.
private void createItemsFromKeyData(List itemList, String action, Object mapping) {
if (itemList != null) {
for (Object item : itemList) {
String nodeKey = generateKey4Map(item, mapping);
if (nodeKey != null) {
Object parentItem = getParentItem(item, mapping);
String parentKey = generateKey4Map(parentItem, mapping);
TreeItem treeItem = createTreeItem(item, mapping);
storeUIAndModel(nodeKey, treeItem, item);
key2Parent.put(nodeKey, parentKey);
}
}
}
}
private Object getParentItem(Object item, Object mapping) {
Object parentItemKey = getKeyValue(item, PARENT_NODE, mapping);
if (parentItemKey != null) {
return items.get(parentItemKey.toString());
}
return null;
}
private void connectItems() {
Iterator<String> itrKeys = key2Parent.keySet().iterator();
while (itrKeys.hasNext()) {
String nodeKey = itrKeys.next();
String parentKey = key2Parent.get(nodeKey);
TreeItem treeItem = (TreeItem)getUIObject(nodeKey);
TreeItem parentTreeItem = (TreeItem)getUIObject(parentKey);
if (treeItem != null) {
addItem(treeItem, parentTreeItem);
}
}
}
private boolean hasStructuredRelation(List itemList) {
if (itemList != null) {
for (Object item : itemList) {
if (isMapContainer(item)) {
//Map map = (Map)item;
Map map = ((DataContainerGVO)item).getDataMap();
Iterator<String> itrKeys = map.keySet().iterator();
while (itrKeys.hasNext()) {
String key = itrKeys.next();
//Object value = getKeyValue(map, key);
Object value = map.get(key);
if (value instanceof DataContainerGVO) {
DataContainerGVO valueGVO = (DataContainerGVO)value;
if (valueGVO.getKind() == DataContainerGVO.KIND_COLLECTION) {
return true;
}
}
}
}
}
}
return false;
}
private void doItemSelection(SelectionEvent<TreeItem> event) {
selectedItem = event.getSelectedItem();
fireItemClickEvent(event);
}
private void fireItemClickEvent(final SelectionEvent<TreeItem> event) {
// When a qaml is listening to a tree with its id, and when clicks on parent tree , as the parent tree is also an item
// we dont have to do implicit click handling of item. otherwise 2 times the event will be executed.
if (isRootItem(event.getSelectedItem())) {
return;
}
if (rootItem instanceof QTreeItem) {
final Label rootItemlabel =((QTreeItem)rootItem).getLabel();
ClickEvent clickEvent = new ClickEvent() {
// This is to make sure that we are making the item id and value available as src-id and src-value
@Override
public Object getSource() {
if (event.getSelectedItem() instanceof QTreeItem) {
return ((QTreeItem)event.getSelectedItem()).getLabel();
}
return rootItemlabel;
}
};
rootItemlabel.fireEvent(clickEvent);
}
}
private TreeItem createTreeItem(String name) {
TreeItem treeItem = new QTreeItem(name, this);
fillInAttributesFromRoot(name, treeItem);
return treeItem;
}
private TreeItem createTreeItem(Object item, Object mapping) {
TreeItemGVO itemGVO = populateGVO(item, mapping);
TreeItem treeItem = new QTreeItem(itemGVO.getDisplayname(), this);
fillInItemProperties(itemGVO, treeItem);
fillInAttributesFromRoot(itemGVO.getId(), treeItem);
return treeItem;
}
private void fillInAttributesFromRoot(String itemId, TreeItem treeItem) {
String uuid = "";
String appContext = "";
String parent = "";
String rootTreeId = RendererHelper.getComponentAttributeValue(this, "id");
itemId = itemId + rootTreeId.substring(rootTreeId.indexOf("|"));
uuid = RendererHelper.getComponentPropertyValue(this, "uuid");
appContext = RendererHelper.getComponentContext(this);
parent = RendererHelper.getParentComponent(this);
Label itemLabel = ((QTreeItem)treeItem).getLabel();
DOM.setElementAttribute(itemLabel.getElement(), "id", itemId);
DOM.setElementProperty(itemLabel.getElement(), "uuid", uuid);
RendererHelper.setComponentContext(itemLabel, appContext);
RendererHelper.setParentComponent(itemLabel, parent);
}
private void fillInItemProperties(TreeItemGVO itemGVO, TreeItem treeItem) {
treeItem.setState(itemGVO.getExpand());
}
private TreeItemGVO populateGVO(Object item, Object mapping) {
Object id = getKeyValue(item, "id", mapping);
Object displayName = getKeyValue(item, "displayname", mapping);
Object disabled = getKeyValue(item, "disabled", mapping);
Object visible = getKeyValue(item, "visible", mapping);
Object tooltip = getKeyValue(item, "tooltip", mapping);
Object value = getKeyValue(item, "value", mapping);
Boolean expand = false;
Object propExpand = getKeyValue(item, "expand", mapping);
if (propExpand instanceof Boolean) {
expand = (Boolean)propExpand;
}
if (displayName == null) {
displayName = new String("Item");
}
TreeItemGVO itemGVO = new TreeItemGVO();
itemGVO.setId(getDataString(id));
itemGVO.setDisplayname(getDataString(displayName));
itemGVO.setExpand(expand);
itemGVO.setTooltip(getDataString(tooltip));
return itemGVO;
}
public String getDataString(Object item) {
String value = null;
if(item instanceof DataContainerGVO) {
value = ((DataContainerGVO)item).getDataString();
}
return value;
}
private void storeUIAndModel(Object key, UIObject uiObject, Object uiObjectModel) {
uiToKey.put(uiObject, key);
keyToUI.put(key, uiObject);
keyToUIModel.put(key, uiObjectModel);
}
private UIObject getUIObject(Object key) {
return keyToUI.get(key);
}
private Object getUIModel(Object key) {
return keyToUIModel.get(key);
}
private String generateKey4Map(Object item, Object mapping) {
return generateKey4Map(item, NODE, mapping);
}
private String generateKey4Map(Object item, String key, Object mapping) {
Object nodeKey = getKeyValue(item, key, mapping);
Object nodeTypeKey = getKeyValue(item, NODE_TYPE, mapping);
if ((nodeKey != null) ) {
return nodeKey + KEY_DELIMITER + nodeTypeKey;
}
return null;
}
private Object getKeyValue(Object item, String key) {
return getKeyValue(item, key, null);
}
private Object getKeyValue(Object item, String key, Object mapping) {
if (isMapContainer(item) && (key != null)) {
//Map map = (Map)item;
Map map = ((DataContainerGVO)item).getDataMap();
key = getMappedKey(key, mapping);
if (map.containsKey(key)) {
return map.get(key);
} else if (map.containsKey(key.toLowerCase())) {
return map.get(key.toLowerCase());
} else if (map.containsKey(key.toUpperCase())) {
return map.get(key.toUpperCase());
}
}
return null;
}
private String getMappedKey(String key, Object mapping) {
String newKey = key;
if (mapping instanceof Map) {
Map map = (Map)mapping;
if (map.containsKey(key)) {
newKey = (String)map.get(key);
}
}
return newKey;
}
public static boolean isSetAction(String action) {
return ((action != null) && (action.equals("set")));
}
/*public static boolean isMap(Object data) {
return (data instanceof Map);
}*/
public static boolean isMapContainer(Object data) {
if(data instanceof DataContainerGVO) {
return (((DataContainerGVO) data).isMap());
}
return false;
}
public static boolean isList(Object data) {
return (data instanceof List);
}
}