/* * RHQ Management Platform * Copyright (C) 2005-2010 Red Hat, Inc. * All rights reserved. * * 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 version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.coregui.client.admin.templates; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.types.TreeModelType; import com.smartgwt.client.widgets.grid.ListGrid; import com.smartgwt.client.widgets.grid.ListGridRecord; import com.smartgwt.client.widgets.tree.Tree; import com.smartgwt.client.widgets.tree.TreeGrid; import com.smartgwt.client.widgets.tree.TreeNode; import org.rhq.core.domain.resource.ResourceCategory; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.domain.resource.composite.ResourceTypeTemplateCountComposite; import org.rhq.core.domain.util.ResourceTypeUtility; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.ImageManager; import org.rhq.coregui.client.Messages; import org.rhq.coregui.client.gwt.GWTServiceLookup; import org.rhq.coregui.client.gwt.ResourceTypeGWTServiceAsync; /** * A helper class for {@link ResourceTypeTreeView}. * * @author Greg Hinkle * @author John Mazzitelli */ public abstract class ResourceTypeTreeNodeBuilder { static private final Messages MSG = CoreGUI.getMessages(); public static final String ATTRIB_ID = "id"; public static final String ATTRIB_CHILDREN = "children"; public static final String ATTRIB_NAME = "name"; public static final String ATTRIB_PLUGIN = "plugin"; public static final String ATTRIB_CATEGORY = "category"; public static final String ATTRIB_EDIT = "edit"; abstract ResourceTypeListGridRecord getGridRecordInstance(ResourceTypeTemplateCountComposite composite); abstract ResourceTypeTreeNode getTreeNodeInstance(ResourceTypeTemplateCountComposite composite, String plugin); public ResourceTypeTreeNodeBuilder(final ListGrid platformsGrid, final ListGrid platformServicesGrid, final TreeGrid serversGrid) { ResourceTypeGWTServiceAsync resourceTypeService = GWTServiceLookup.getResourceTypeGWTService(30000); resourceTypeService .getTemplateCountCompositeMap(new AsyncCallback<Map<Integer, ResourceTypeTemplateCountComposite>>() { @Override public void onSuccess(Map<Integer, ResourceTypeTemplateCountComposite> result) { // result contains all of our resource types, including the parent hierarchy HashSet<ResourceTypeListGridRecord> platformsRecords; HashSet<ResourceTypeListGridRecord> platformServicesRecords; HashMap<Integer, ResourceTypeTreeNode> serversNodes; // all server nodes (top level and below) HashSet<Integer> topServers; // those servers that are at the root of the tree HashMap<Integer, ArrayList<Integer>> childrenGraph; // defines the children of all server nodes platformsRecords = new HashSet<ResourceTypeListGridRecord>(); platformServicesRecords = new HashSet<ResourceTypeListGridRecord>(); serversNodes = new HashMap<Integer, ResourceTypeTreeNode>(); topServers = new HashSet<Integer>(); childrenGraph = new HashMap<Integer, ArrayList<Integer>>(); for (ResourceTypeTemplateCountComposite composite : result.values()) { ResourceType type = composite.getType(); Set<ResourceType> parentTypes = type.getParentResourceTypes(); if (isEmpty(parentTypes)) { if (type.getCategory() == ResourceCategory.PLATFORM) { // no parents but is a platform - these are our main, top-level platforms platformsRecords.add(getGridRecordInstance(composite)); } else { // no parents but not a platform - these are our top-level servers ResourceTypeTreeNode node = getTreeNodeInstance(composite, type.getPlugin()); topServers.add(node.getResourceTypeId()); serversNodes.put(node.getResourceTypeId(), node); } } else { // has parents; if all the direct parents are top level platforms // and the category is service, consider it a "special" platform service boolean isPlatformService = true; // assume its one, unless one of its parents is not a top level platform if (type.getCategory() == ResourceCategory.SERVICE) { for (ResourceType parentType : parentTypes) { // if one of its parents is not a platform or one of its parent has parents itself, // then this is not a platform service if ((parentType.getCategory() != ResourceCategory.PLATFORM) || !isEmpty(parentType.getParentResourceTypes())) { isPlatformService = false; break; } } } else { isPlatformService = false; // can't be a platform service, its not in the SERVICE category } if (isPlatformService) { platformServicesRecords.add(getGridRecordInstance(composite)); } else { // In some cases, a top level server is limited to which platforms it can run on. // Therefore, the parents will not be null/empty (as would be the case if the top level // server can run on ALL platforms), but instead it will have the subset of platforms // the type is valid on. But its the same type - so we only want to show it once. // This is what gotPlatform tracks - whether we saw a parent platform or not. // // But we also have the case where a server type can run inside multiple parent server types. // We want to show these under all their parents to make it easier for the user to find them. boolean gotPlatform = false; for (ResourceType parentType : type.getParentResourceTypes()) { boolean isParentAPlatform = (parentType.getCategory() == ResourceCategory.PLATFORM && isEmpty(parentType .getParentResourceTypes())); if (!isParentAPlatform || !gotPlatform) { int parentId = parentType.getId(); String parentIdString = String.valueOf(parentId); ResourceTypeTreeNode node = getTreeNodeInstance(composite, parentIdString); serversNodes.put(node.getResourceTypeId(), node); if (isParentAPlatform) { topServers.add(node.getResourceTypeId()); } else { // we are a child to other type, add it to the list of children ArrayList<Integer> childList = childrenGraph.get(parentId); if (childList == null) { childList = new ArrayList<Integer>(); childrenGraph.put(parentId, childList); } childList.add(node.getResourceTypeId()); } } if (isParentAPlatform) { gotPlatform = true; } } } } } // now set up our UI components to show the data platformsGrid.setSortField(ATTRIB_NAME); platformServicesGrid.setSortField(ATTRIB_NAME); serversGrid.setSortField(ATTRIB_NAME); platformsGrid.setData(platformsRecords.toArray(new ListGridRecord[platformsRecords.size()])); platformServicesGrid.setData(platformServicesRecords .toArray(new ListGridRecord[platformServicesRecords.size()])); Tree tree = new Tree(); tree.setModelType(TreeModelType.CHILDREN); tree.setChildrenProperty(ATTRIB_CHILDREN); TreeNode rootNode = new TreeNode("0"); tree.setRoot(rootNode); for (Integer topServerId : topServers) { ResourceTypeTreeNode topServerNode = serversNodes.get(topServerId); topServerNode = topServerNode.copy(); fillHierarchy(topServerNode, serversNodes, childrenGraph); tree.add(topServerNode, rootNode); } serversGrid.setData(tree); } private void fillHierarchy(ResourceTypeTreeNode node, HashMap<Integer, ResourceTypeTreeNode> serversNodes, HashMap<Integer, ArrayList<Integer>> childrenGraph) { if (node.getChildren().length > 0) { return; // we've already populated this node's children before; nothing to do } ArrayList<Integer> childrenIds = childrenGraph.get(node.getResourceTypeId()); if (childrenIds != null) { for (Integer childrenId : childrenIds) { ResourceTypeTreeNode childNode = serversNodes.get(childrenId); if (childNode != null) { // this should never be null, but this let's us continue if we have a bug childNode = childNode.copy(); fillHierarchy(childNode, serversNodes, childrenGraph); node.addChild(childNode); } } } return; } @Override public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.widget_typeTree_loadFail(), caught); } }); } private boolean isEmpty(Set<ResourceType> set) { return set == null || set.isEmpty(); } public static class ResourceTypeListGridRecord extends ListGridRecord { private int id; protected ResourceTypeListGridRecord(ResourceTypeTemplateCountComposite composite) { ResourceType resourceType = composite.getType(); this.id = resourceType.getId(); setAttribute(ATTRIB_ID, String.valueOf(id)); setAttribute(ATTRIB_NAME, ResourceTypeUtility.displayName(resourceType)); setAttribute(ATTRIB_PLUGIN, resourceType.getPlugin()); setAttribute(ATTRIB_CATEGORY, resourceType.getCategory().name()); setAttribute(ATTRIB_EDIT, ImageManager.getEditIcon()); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ResourceTypeListGridRecord)) { return false; } return (this.id == ((ResourceTypeListGridRecord) o).id); } @Override public int hashCode() { return 31 * id; } } public static class ResourceTypeTreeNode extends TreeNode { private int id; private String parentId; private TreeNode[] children; private ResourceTypeTreeNode() { // for use by copy() method } protected ResourceTypeTreeNode(ResourceTypeTemplateCountComposite composite, String parentId) { ResourceType resourceType = composite.getType(); this.id = resourceType.getId(); this.parentId = parentId; setAttribute(ATTRIB_ID, id); setAttribute(ATTRIB_NAME, ResourceTypeUtility.displayName(resourceType)); setAttribute(ATTRIB_PLUGIN, resourceType.getPlugin()); setAttribute(ATTRIB_CATEGORY, resourceType.getCategory().name()); setAttribute(ATTRIB_EDIT, ImageManager.getEditIcon()); setChildren(new TreeNode[0]); } public int getResourceTypeId() { return this.id; } public TreeNode[] getChildren() { return this.children; } @Override public void setChildren(TreeNode[] children) { this.children = children; super.setChildren(children); } public void addChild(TreeNode newChild) { TreeNode[] newChildren = new TreeNode[this.children.length + 1]; System.arraycopy(this.children, 0, newChildren, 0, this.children.length); newChildren[this.children.length] = newChild; setChildren(newChildren); } // clone this object and return it - subclasses should override this to copy their own attributes public ResourceTypeTreeNode copy() { ResourceTypeTreeNode dup = new ResourceTypeTreeNode(); dup.id = this.id; dup.parentId = this.parentId; dup.children = this.children; dup.setAttribute(ATTRIB_ID, this.getAttributeAsInt(ATTRIB_ID)); dup.setAttribute(ATTRIB_NAME, this.getAttribute(ATTRIB_NAME)); dup.setAttribute(ATTRIB_PLUGIN, this.getAttribute(ATTRIB_PLUGIN)); dup.setAttribute(ATTRIB_CATEGORY, this.getAttribute(ATTRIB_CATEGORY)); dup.setAttribute(ATTRIB_EDIT, this.getAttribute(ATTRIB_EDIT)); return dup; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ResourceTypeTreeNode)) { return false; } ResourceTypeTreeNode that = (ResourceTypeTreeNode) o; if (this.id != that.id) { return false; } if (this.parentId == null) { return that.parentId == null; } return this.parentId.equals(that.parentId); } @Override public int hashCode() { int result = 31; result = result * id; result = result + (parentId != null ? parentId.hashCode() : 0); return result; } } }