/*
* 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.components.configuration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.CloseClickEvent;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tree.Tree;
import com.smartgwt.client.widgets.tree.TreeGrid;
import com.smartgwt.client.widgets.tree.TreeGridField;
import com.smartgwt.client.widgets.tree.TreeNode;
import org.rhq.core.domain.configuration.AbstractPropertyMap;
import org.rhq.core.domain.configuration.AbstractResourceConfigurationUpdate;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.ResourceConfigurationUpdate;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionList;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionMap;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.core.domain.configuration.definition.PropertyGroupDefinition;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.Messages;
import org.rhq.coregui.client.inventory.resource.type.ResourceTypeRepository;
/**
* @author Greg Hinkle
*/
public class ConfigurationComparisonView extends VLayout {
private static final String ATTRIB_ALL_SAME = "consistent";
private static final Messages MSG = CoreGUI.getMessages();
private ConfigurationDefinition definition;
private List<Configuration> configs;
private List<String> titles;
public ConfigurationComparisonView(ConfigurationDefinition definition, List<Configuration> configs,
List<String> titles) {
this.definition = definition;
this.configs = configs;
this.titles = titles;
setWidth100();
}
@Override
protected void onDraw() {
super.onDraw();
TreeGrid treeGrid = new TreeGrid();
treeGrid.setWidth100();
treeGrid.setLoadDataOnDemand(false);
TreeGridField[] fields = new TreeGridField[2 + titles.size()];
TreeGridField nameField = new TreeGridField("name", MSG.common_title_name(), 250);
nameField.setFrozen(true);
nameField.setCellFormatter(new CellFormatter() {
public String format(Object o, ListGridRecord listGridRecord, int i, int i1) {
if (listGridRecord == null || listGridRecord.getAttributeAsBoolean(ATTRIB_ALL_SAME)) {
return String.valueOf(o);
} else {
return "<span style=\"color: red;\">" + String.valueOf(o) + "</span>";
}
}
});
TreeGridField typeField = new TreeGridField("type", MSG.common_title_type(), 80);
fields[0] = nameField;
fields[1] = typeField;
int i = 2;
for (String title : titles) {
TreeGridField columnField = new TreeGridField(title, title, 150);
columnField.setCellFormatter(new CellFormatter() {
public String format(Object o, ListGridRecord listGridRecord, int i, int i1) {
if (!(listGridRecord instanceof ComparisonTreeNode)) {
return "";
} else if (listGridRecord.getAttributeAsBoolean(ATTRIB_ALL_SAME)) {
return String.valueOf(o);
} else {
return "<span style=\"color: red;\">" + String.valueOf(o) + "</span>";
}
}
});
fields[i++] = columnField;
}
treeGrid.setFields(fields);
treeGrid.setData(buildTree());
addMember(treeGrid);
}
protected Tree buildTree() {
Tree tree = new Tree();
TreeNode root = new TreeNode(MSG.view_configCompare_configCompare());
ArrayList<TreeNode> children = new ArrayList<TreeNode>();
List<PropertyDefinition> nonGroupDefs = definition.getNonGroupedProperties();
if (nonGroupDefs != null && !nonGroupDefs.isEmpty()) {
TreeNode groupNode = new TreeNode(MSG.common_title_generalProp());
buildNode(groupNode, nonGroupDefs, configs);
children.add(groupNode);
}
for (PropertyGroupDefinition group : definition.getGroupDefinitions()) {
TreeNode groupNode = new TreeNode(group.getDisplayName());
buildNode(groupNode, definition.getPropertiesInGroup(group.getName()), configs);
children.add(groupNode);
}
root.setChildren(children.toArray(new TreeNode[children.size()]));
tree.setRoot(root);
return tree;
}
private void buildNode(TreeNode parent, Collection<PropertyDefinition> definitions,
List<? extends AbstractPropertyMap> maps) {
ArrayList<TreeNode> children = new ArrayList<TreeNode>();
parent.setAttribute(ATTRIB_ALL_SAME, true);
for (PropertyDefinition definition : definitions) {
if (definition instanceof PropertyDefinitionSimple) {
ArrayList<PropertySimple> properties = new ArrayList<PropertySimple>();
for (AbstractPropertyMap map : maps) {
properties.add(map.getSimple(definition.getName()));
}
ComparisonTreeNode node = new ComparisonTreeNode((PropertyDefinitionSimple) definition, properties,
titles);
if (!node.getAttributeAsBoolean(ATTRIB_ALL_SAME)) {
parent.setAttribute(ATTRIB_ALL_SAME, false);
}
children.add(node);
} else if (definition instanceof PropertyDefinitionMap) {
PropertyDefinitionMap defMap = (PropertyDefinitionMap) definition;
TreeNode mapNode = new TreeNode(defMap.getDisplayName());
ArrayList<PropertyMap> properties = new ArrayList<PropertyMap>();
for (AbstractPropertyMap map : maps) {
if (map instanceof PropertyMap) {
properties.add((PropertyMap) map);
} else {
// map is a Configuration
String name = definition.getName();
PropertyMap innerMap = map.getMap(name);
properties.add(innerMap);
// TODO recursively add the map's items
}
}
buildNode(mapNode, defMap.getOrderedPropertyDefinitions(), properties);
if (!mapNode.getAttributeAsBoolean(ATTRIB_ALL_SAME)) {
parent.setAttribute(ATTRIB_ALL_SAME, false);
}
children.add(mapNode);
} else if (definition instanceof PropertyDefinitionList) {
PropertyDefinitionList defList = (PropertyDefinitionList) definition;
TreeNode listNode = new TreeNode(defList.getDisplayName());
listNode.setAttribute(ATTRIB_ALL_SAME, true);
if (defList.getMemberDefinition() instanceof PropertyDefinitionMap) { // support list-o-maps only
PropertyDefinition memberDef = defList.getMemberDefinition();
Collection<PropertyDefinition> memberDefColl = new ArrayList<PropertyDefinition>(1);
memberDefColl.add(memberDef);
int max = 0; // will be the largest size of any of our lists that are being compared
for (AbstractPropertyMap map : maps) {
try {
int size = map.getList(defList.getName()).getList().size();
if (size > max) {
max = size;
}
} catch (Throwable t) {
// paranoia - just skip so we don't kill entire compare window if our config doesn't have proper list-o-map
}
}
ArrayList<TreeNode> innerChildren = new ArrayList<TreeNode>();
for (int i = 0; i < max; i++) {
TreeNode listItemNode = new TreeNode(String.valueOf(i));
ArrayList<PropertyMap> properties = new ArrayList<PropertyMap>();
for (AbstractPropertyMap map : maps) {
try {
List<Property> list = map.getList(defList.getName()).getList();
if (list.size() < (i + 1)) {
properties.add(new PropertyMap()); // this list didn't have an i-th item, just use an empty map
} else {
properties.add((PropertyMap) list.get(i));
}
} catch (Throwable t) {
// paranoia - just skip so we don't kill entire compare window if our config doesn't have proper list-o-map
properties.add(new PropertyMap());
}
}
buildNode(listItemNode, memberDefColl, properties);
if (!listItemNode.getAttributeAsBoolean(ATTRIB_ALL_SAME)) {
parent.setAttribute(ATTRIB_ALL_SAME, false);
listNode.setAttribute(ATTRIB_ALL_SAME, false); // any diffs always causes this to indicate the diff
}
innerChildren.add(listItemNode);
}
listNode.setChildren(innerChildren.toArray(new TreeNode[innerChildren.size()]));
}
children.add(listNode);
}
}
parent.setChildren(children.toArray(new TreeNode[children.size()]));
}
public static void displayComparisonDialog(final ArrayList<? extends AbstractResourceConfigurationUpdate> configs) {
AbstractResourceConfigurationUpdate theFirstUpdateItem = configs.get(0);
int resourceId = theFirstUpdateItem.getResource().getResourceType().getId();
final boolean isResourceConfig = theFirstUpdateItem instanceof ResourceConfigurationUpdate;
ResourceTypeRepository.Cache.getInstance().getResourceTypes(
resourceId,
isResourceConfig ? EnumSet.of(ResourceTypeRepository.MetadataType.resourceConfigurationDefinition)
: EnumSet.of(ResourceTypeRepository.MetadataType.pluginConfigurationDefinition),
new ResourceTypeRepository.TypeLoadedCallback() {
public void onTypesLoaded(ResourceType type) {
ConfigurationDefinition definition;
if (isResourceConfig) {
definition = type.getResourceConfigurationDefinition();
} else {
definition = type.getPluginConfigurationDefinition();
}
ArrayList<Configuration> configurations = new ArrayList<Configuration>();
ArrayList<String> titles = new ArrayList<String>();
for (AbstractResourceConfigurationUpdate update : configs) {
configurations.add(update.getConfiguration());
titles.add(String.valueOf(update.getId()));
}
displayComparisonDialog(definition, configurations, titles);
}
});
}
public static void displayComparisonDialog(ConfigurationDefinition definition,
ArrayList<Configuration> configurations, ArrayList<String> titles) {
ConfigurationComparisonView view = new ConfigurationComparisonView(definition, configurations, titles);
final Window dialog = new Window();
dialog.setTitle(MSG.view_configCompare_comparingConfigs());
dialog.setShowMinimizeButton(true);
dialog.setShowMaximizeButton(true);
dialog.setWidth(700);
dialog.setHeight(500);
dialog.setIsModal(true);
dialog.setShowModalMask(true);
dialog.setShowResizer(true);
dialog.setShowFooter(true);
dialog.setCanDragResize(true);
dialog.setAutoCenter(true);
dialog.centerInPage();
dialog.addItem(view);
dialog.addCloseClickHandler(new CloseClickHandler() {
@Override
public void onCloseClick(CloseClickEvent event) {
dialog.markForDestroy();
}
});
dialog.show();
}
private static class ComparisonTreeNode extends TreeNode {
private ComparisonTreeNode(PropertyDefinitionSimple definition, List<PropertySimple> properties,
List<String> titles) {
super(definition.getDisplayName());
setAttribute("type", definition.getType().name());
int i = 0;
boolean allTheSame = true;
String commonValue = null;
for (PropertySimple prop : properties) {
String value = prop != null ? prop.getStringValue() : null;
if (i == 0) {
commonValue = value;
} else if (allTheSame && commonValue == null && value != null
|| (commonValue != null && !commonValue.equals(value))) {
allTheSame = false;
}
setAttribute(titles.get(i++), value);
setAttribute(ATTRIB_ALL_SAME, allTheSame);
}
}
}
}