/*
* RHQ Management Platform
* Copyright 2011, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* 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.drift;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.rpc.RPCResponse;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.Autofit;
import com.smartgwt.client.types.ExpansionMode;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordCollapseEvent;
import com.smartgwt.client.widgets.grid.events.RecordCollapseHandler;
import com.smartgwt.client.widgets.grid.events.RecordExpandEvent;
import com.smartgwt.client.widgets.grid.events.RecordExpandHandler;
import com.smartgwt.client.widgets.layout.Layout;
import org.rhq.core.domain.criteria.DriftDefinitionCriteria;
import org.rhq.core.domain.criteria.DriftDefinitionTemplateCriteria;
import org.rhq.core.domain.criteria.GenericDriftChangeSetCriteria;
import org.rhq.core.domain.drift.Drift;
import org.rhq.core.domain.drift.DriftDefinition;
import org.rhq.core.domain.drift.DriftDefinitionTemplate;
import org.rhq.core.domain.drift.DriftSnapshot;
import org.rhq.core.domain.drift.DriftSnapshotRequest;
import org.rhq.core.domain.util.PageList;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.ImageManager;
import org.rhq.coregui.client.LinkManager;
import org.rhq.coregui.client.components.table.AbstractTableAction;
import org.rhq.coregui.client.components.table.Table;
import org.rhq.coregui.client.components.table.TableActionEnablement;
import org.rhq.coregui.client.components.table.TimestampCellFormatter;
import org.rhq.coregui.client.drift.wizard.DriftPinTemplateWizard;
import org.rhq.coregui.client.gwt.DriftGWTServiceAsync;
import org.rhq.coregui.client.gwt.GWTServiceLookup;
import org.rhq.coregui.client.util.RPCDataSource;
import org.rhq.coregui.client.util.enhanced.EnhancedIButton.ButtonColor;
import org.rhq.coregui.client.util.message.Message;
/**
* A view that displays a nested table view of a snapshot. The outer table is a list of directories in the snapshot
* and the inner table displays the files for that directory. Data is fetched as needed to avoid large fetched for
* snapshots involving potentially thousands of files.
*
* @author Jay Shaughnessy
*/
public class DriftSnapshotView extends Table<DriftSnapshotDataSource> {
private static final String DEFAULT_TITLE = MSG.view_drift_table_snapshot();
private Integer templateId;
private Integer driftDefId;
private Integer resourceId;
private Integer version;
private boolean hasWriteAccess;
private boolean pinnedToTemplate;
private String templateChangeSetId;
protected DriftSnapshotDataSource dataSource;
protected DriftSnapshotListGrid listGrid;
public DriftSnapshotView(String tableTitle, int templateId, boolean hasWriteAccess) {
this((null == tableTitle) ? DEFAULT_TITLE : tableTitle, templateId, null, null, null, hasWriteAccess);
}
public DriftSnapshotView(String tableTitle, int resourceId, int driftDefId, int version, boolean hasWriteAccess) {
this((null == tableTitle) ? DEFAULT_TITLE : tableTitle, null, resourceId, driftDefId, version, hasWriteAccess);
}
private DriftSnapshotView(String tableTitle, Integer templateId, Integer resourceId, Integer driftDefId,
Integer version, boolean hasWriteAccess) {
super(tableTitle);
this.templateId = templateId;
this.resourceId = resourceId;
this.driftDefId = driftDefId;
this.version = (null == version) ? 0 : version;
this.hasWriteAccess = hasWriteAccess;
setDataSource(getDataSource());
}
@Override
public DriftSnapshotDataSource getDataSource() {
if (null == dataSource) {
if (null != templateId) {
dataSource = new DriftSnapshotDataSource(templateId);
} else {
dataSource = new DriftSnapshotDataSource(driftDefId, version);
}
}
return dataSource;
}
@Override
protected ListGrid createListGrid() {
return new DriftSnapshotListGrid();
}
@Override
protected void onDraw() {
// Drift def snapshot view or template pinned snapshot?
if (null != this.driftDefId) {
DriftDefinitionCriteria defCriteria = new DriftDefinitionCriteria();
defCriteria.addFilterId(driftDefId);
defCriteria.fetchConfiguration(true);
defCriteria.fetchTemplate(true);
DriftGWTServiceAsync driftService = GWTServiceLookup.getDriftService();
driftService.findDriftDefinitionsByCriteria(defCriteria, new AsyncCallback<PageList<DriftDefinition>>() {
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(MSG.view_drift_failure_load(), caught);
}
public void onSuccess(PageList<DriftDefinition> result) {
DriftDefinition driftDef = result.get(0);
String defName = driftDef.getName();
String title;
if (0 == version) {
String isPinned = String.valueOf(driftDef.isPinned());
title = MSG.view_drift_table_title_initialSnapshot(defName, isPinned);
} else {
title = MSG.view_drift_table_title_snapshot(String.valueOf(version), defName);
}
setTitleString(title);
pinnedToTemplate = driftDef.getTemplate() != null && driftDef.getTemplate().isPinned();
DriftSnapshotView.super.onDraw();
}
});
} else {
DriftDefinitionTemplateCriteria templateCriteria = new DriftDefinitionTemplateCriteria();
templateCriteria.addFilterId(templateId);
templateCriteria.fetchResourceType(true);
DriftGWTServiceAsync driftService = GWTServiceLookup.getDriftService();
driftService.findDriftDefinitionTemplatesByCriteria(templateCriteria,
new AsyncCallback<PageList<DriftDefinitionTemplate>>() {
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(MSG.view_drift_failure_load(), caught);
}
public void onSuccess(PageList<DriftDefinitionTemplate> result) {
DriftDefinitionTemplate template = result.get(0);
String templateName = template.getName();
String title = MSG.view_drift_table_title_templateSnapshot(templateName);
setTitleString(title);
DriftSnapshotView.super.onDraw();
}
});
}
}
@Override
protected void configureTable() {
ListGrid grid = getListGrid();
ArrayList<ListGridField> dataSourceFields = getDataSource().getListGridFields();
grid.setFields(dataSourceFields.toArray(new ListGridField[dataSourceFields.size()]));
setupTableInteractions(this.hasWriteAccess);
super.configureTable();
}
protected void setupTableInteractions(final boolean hasWriteAccess) {
TableActionEnablement pinEnablement = hasWriteAccess ? TableActionEnablement.ALWAYS
: TableActionEnablement.NEVER;
addTableAction(MSG.view_drift_button_pinToDef(), MSG.view_drift_button_pinToDef_confirm(), ButtonColor.BLUE,
new AbstractTableAction(pinEnablement) {
public void executeAction(ListGridRecord[] selection, Object actionValue) {
pinToDefinition();
}
@Override
public boolean isEnabled(ListGridRecord[] selection) {
return !pinnedToTemplate && super.isEnabled(selection);
}
});
addTableAction(MSG.view_drift_button_pinToTemplate(), MSG.view_drift_button_pinToTemplate_confirm(),
ButtonColor.BLUE, new AbstractTableAction(pinEnablement) {
public void executeAction(ListGridRecord[] selection, Object actionValue) {
pinToTemplate();
}
});
}
private void pinToDefinition() {
GWTServiceLookup.getDriftService().pinSnapshot(driftDefId, version, new AsyncCallback<Void>() {
public void onSuccess(Void x) {
CoreGUI.getMessageCenter().notify(
new Message(MSG.view_drift_success_pinToDef(String.valueOf(version)), Message.Severity.Info));
CoreGUI.goToView(LinkManager.getDriftDefinitionsLink(resourceId));
}
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(MSG.view_drift_failure_pinToDef(), caught);
}
});
}
private void pinToTemplate() {
DriftPinTemplateWizard.showWizard(driftDefId, version);
// we can refresh the table buttons immediately since the wizard is a dialog, the
// user can't access enabled buttons anyway.
DriftSnapshotView.this.refreshTableInfo();
}
private class DriftSnapshotListGrid extends ListGrid {
public DriftSnapshotListGrid() {
super();
setCanExpandRecords(true);
setCanExpandMultipleRecords(true);
setExpansionMode(ExpansionMode.RELATED);
}
@Override
protected Canvas getExpansionComponent(ListGridRecord record) {
String dirPath = record.getAttribute(DriftSnapshotDataSource.ATTR_DIR_PATH);
return new DirectoryView(dirPath);
}
}
public class DirectoryView extends Table<DirectoryView.DirectoryViewDataSource> {
private String directory;
private DirectoryViewDataSource dataSource;
public DirectoryView(String directory) {
super(null, true);
this.directory = directory;
setShowFilterForm(false);
setShowHeader(false);
setShowFooter(false);
setDataSource(getDataSource());
}
@Override
public DirectoryViewDataSource getDataSource() {
if (null == dataSource) {
dataSource = new DirectoryViewDataSource();
}
return dataSource;
}
@Override
protected void configureTable() {
ArrayList<ListGridField> dataSourceFields = getDataSource().getListGridFields();
getListGrid().setFields(dataSourceFields.toArray(new ListGridField[dataSourceFields.size()]));
super.configureTable();
}
@Override
protected void configureTableContents(Layout contents) {
contents.setWidth100();
contents.setHeight100();
contents.setOverflow(Overflow.VISIBLE);
}
@Override
protected void configureListGrid(ListGrid grid) {
grid.setDefaultHeight(1);
grid.setAutoFitData(Autofit.VERTICAL);
}
@Override
protected ListGrid createListGrid() {
return new DirectoryViewListGrid();
}
private class DirectoryViewListGrid extends ListGrid {
public DirectoryViewListGrid() {
super();
setCanExpandRecords(true);
setCanExpandMultipleRecords(true);
setExpansionMode(ExpansionMode.RELATED);
addRecordExpandHandler(new RecordExpandHandler() {
@Override
public void onRecordExpand(RecordExpandEvent event) {
event.getRecord().setAttribute("Expanded", true);
}
});
addRecordCollapseHandler(new RecordCollapseHandler() {
@Override
public void onRecordCollapse(RecordCollapseEvent event) {
event.getRecord().setAttribute("Expanded", false);
}
});
}
@Override
protected String getCellCSSText(ListGridRecord record, int rowNum, int colNum) {
if (Boolean.TRUE.equals(record.getAttributeAsBoolean("Expanded"))) {
return "background-color:#ffffcc;";
}
return super.getCellCSSText(record, rowNum, colNum);
}
@Override
protected Canvas getExpansionComponent(ListGridRecord record) {
String driftId = record.getAttribute(DriftDataSource.ATTR_ID);
return new DriftSnapshotDriftDetailsView(driftId);
}
}
public class DirectoryViewDataSource extends RPCDataSource<Drift<?, ?>, GenericDriftChangeSetCriteria> {
public DirectoryViewDataSource() {
addDataSourceFields();
}
/**
* The view that contains the list grid which will display this datasource's data will call this
* method to get the field information which is used to control the display of the data.
*
* @return list grid fields used to display the datasource data
*/
public ArrayList<ListGridField> getListGridFields() {
ArrayList<ListGridField> fields = new ArrayList<ListGridField>(7);
ListGridField ctimeField = new ListGridField(DriftDataSource.ATTR_CTIME, MSG.common_title_createTime());
ctimeField.setCellFormatter(new TimestampCellFormatter());
ctimeField.setShowHover(true);
ctimeField.setHoverCustomizer(TimestampCellFormatter.getHoverCustomizer(DriftDataSource.ATTR_CTIME));
fields.add(ctimeField);
ListGridField categoryField = new ListGridField(DriftDataSource.ATTR_CATEGORY,
MSG.common_title_category());
categoryField.setType(ListGridFieldType.IMAGE);
categoryField.setAlign(Alignment.CENTER);
categoryField.setShowHover(true);
categoryField.setHoverCustomizer(new HoverCustomizer() {
@Override
public String hoverHTML(Object value, ListGridRecord record, int rowNum, int colNum) {
String cat = record.getAttribute(DriftDataSource.ATTR_CATEGORY);
if (DriftDataSource.CATEGORY_ICON_ADD.equals(cat)) {
return MSG.view_drift_category_fileAdded();
} else if (DriftDataSource.CATEGORY_ICON_CHANGE.equals(cat)) {
return MSG.view_drift_category_fileChanged();
} else if (DriftDataSource.CATEGORY_ICON_REMOVE.equals(cat)) {
return MSG.view_drift_category_fileRemoved();
} else if (DriftDataSource.CATEGORY_ICON_NEW.equals(cat)) {
return MSG.view_drift_category_fileNew();
} else {
return ""; // will never get here
}
}
});
fields.add(categoryField);
ListGridField changeSetVersionField = new ListGridField(DriftDataSource.ATTR_CHANGESET_VERSION,
MSG.view_drift_table_snapshot());
fields.add(changeSetVersionField);
ListGridField pathField = new ListGridField(DriftDataSource.ATTR_PATH, MSG.common_title_name());
fields.add(pathField);
ctimeField.setWidth(200);
categoryField.setWidth(100);
changeSetVersionField.setWidth(100);
pathField.setWidth("*");
return fields;
}
@Override
protected void executeFetch(final DSRequest request, final DSResponse response,
GenericDriftChangeSetCriteria criteria) {
if (null == templateId) {
DriftSnapshotRequest snapshotRequest = new DriftSnapshotRequest(driftDefId, version, null,
directory, false, true);
executeGetSnapshot(request, response, snapshotRequest);
} else {
if (null == templateChangeSetId) {
DriftDefinitionTemplateCriteria templateCriteria = new DriftDefinitionTemplateCriteria();
templateCriteria.addFilterId(templateId);
GWTServiceLookup.getDriftService().findDriftDefinitionTemplatesByCriteria(templateCriteria,
new AsyncCallback<PageList<DriftDefinitionTemplate>>() {
public void onSuccess(final PageList<DriftDefinitionTemplate> result) {
templateChangeSetId = String.valueOf(result.get(0).getChangeSetId());
DriftSnapshotRequest snapshotRequest = new DriftSnapshotRequest(
templateChangeSetId, directory, false, true);
executeGetSnapshot(request, response, snapshotRequest);
}
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError("Failed to load definition.", caught);
}
});
} else {
DriftSnapshotRequest snapshotRequest = new DriftSnapshotRequest(templateChangeSetId, directory,
false, true);
executeGetSnapshot(request, response, snapshotRequest);
}
}
}
private void executeGetSnapshot(final DSRequest request, final DSResponse response,
DriftSnapshotRequest snapshotRequest) {
DriftGWTServiceAsync driftService = GWTServiceLookup.getDriftService();
driftService.getSnapshot(snapshotRequest, new AsyncCallback<DriftSnapshot>() {
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(MSG.view_drift_failure_load(), caught);
response.setStatus(RPCResponse.STATUS_FAILURE);
processResponse(request.getRequestId(), response);
}
public void onSuccess(DriftSnapshot result) {
Collection<Drift<?, ?>> drifts = result.getDriftInstances();
ListGridRecord[] records = buildRecords(drifts);
response.setData(records);
response.setTotalRows(drifts.size());
processResponse(request.getRequestId(), response);
}
});
}
@Override
protected GenericDriftChangeSetCriteria getFetchCriteria(DSRequest request) {
return null;
}
@Override
public Drift<?, ?> copyValues(Record from) {
return null;
}
@Override
public ListGridRecord copyValues(Drift<?, ?> from) {
ListGridRecord record = new ListGridRecord();
record.setAttribute(DriftDataSource.ATTR_ID, from.getId());
record.setAttribute(DriftDataSource.ATTR_CTIME, new Date(from.getCtime()));
record.setAttribute(DriftDataSource.ATTR_CATEGORY,
ImageManager.getDriftCategoryIcon((0 == version) ? null : from.getCategory()));
record.setAttribute(DriftDataSource.ATTR_CHANGESET_VERSION, (null == from.getChangeSet()) ? "0" : from
.getChangeSet().getVersion());
record.setAttribute(DriftDataSource.ATTR_PATH, getFileName(from.getPath(), "/"));
return record;
}
}
}
/**
* Return just the filename portion (the portion right of the last path separator string)
* @param path
* @param separator
* @return null if path is null, otherwise the trimmed filename
*/
public static String getFileName(String path, String separator) {
if (null == path) {
return null;
}
int i = path.lastIndexOf(separator);
return (i < 0) ? path.trim() : path.substring(++i).trim();
}
}