package com.tibco.as.spacebar.ui.editor;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.columnChooser.command.DisplayColumnChooserCommandHandler;
import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IConfiguration;
import org.eclipse.nebula.widgets.nattable.copy.command.CopyDataCommandHandler;
import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsDataProvider;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.resize.action.AutoResizeColumnAction;
import org.eclipse.nebula.widgets.nattable.resize.action.ColumnResizeCursorAction;
import org.eclipse.nebula.widgets.nattable.resize.event.ColumnResizeEventMatcher;
import org.eclipse.nebula.widgets.nattable.resize.mode.ColumnResizeDragMode;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
import org.eclipse.nebula.widgets.nattable.style.theme.DarkNatTableThemeConfiguration;
import org.eclipse.nebula.widgets.nattable.style.theme.DefaultNatTableThemeConfiguration;
import org.eclipse.nebula.widgets.nattable.style.theme.ModernNatTableThemeConfiguration;
import org.eclipse.nebula.widgets.nattable.style.theme.ThemeConfiguration;
import org.eclipse.nebula.widgets.nattable.tooltip.NatTableContentTooltip;
import org.eclipse.nebula.widgets.nattable.ui.action.ClearCursorAction;
import org.eclipse.nebula.widgets.nattable.ui.action.NoOpMouseAction;
import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
import org.eclipse.nebula.widgets.nattable.ui.matcher.KeyEventMatcher;
import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.util.concurrent.Lock;
import com.tibco.as.io.AbstractExporter;
import com.tibco.as.io.IOutputStream;
import com.tibco.as.space.FieldDef;
import com.tibco.as.space.Metaspace;
import com.tibco.as.space.SpaceDef;
import com.tibco.as.spacebar.ui.SpaceBarPlugin;
import com.tibco.as.spacebar.ui.model.Space;
import com.tibco.as.spacebar.ui.preferences.Preferences;
import com.tibco.as.spacebar.ui.transfer.MetaspaceTransferJob;
import com.tibco.as.util.Utils;
public abstract class AbstractBrowser<T extends Map<String, Object>> extends
EditorPart implements IOutputStream<T> {
public static final String EDITOR_ID_NEW = "com.tibco.as.spacebar.ui.space.browser.new";
public static final String EDITOR_ID_ALL = "com.tibco.as.spacebar.ui.space.browser.all";
public static final String EDITOR_ID_SNAPSHOT = "com.tibco.as.spacebar.ui.space.browser.snapshot";
public static final String EDITOR_ID_CURRENT = "com.tibco.as.spacebar.ui.space.browser.current";
private NatTable natTable;
private EventList<T> eventList = GlazedLists.threadSafeList(GlazedLists
.eventList(new LinkedList<T>()));
private FilterList<T> filterList = new FilterList<T>(eventList);
private SortedList<T> sortedList = new SortedList<T>(filterList, null);
private BodyLayerStack bodyLayer;
private CopyDataAction copyDataAction;
private FieldDef[] fieldDefs;
private String[] fieldNames;
private String[] nonKeyFieldNames;
private MetaspaceTransferJob browseJob;
private GlazedListsDataProvider<T> bodyDataProvider;
private GlazedListsEventLayer<T> glazedListsEventLayer;
private IUniqueIndexLayer layer;
private DataLayer bodyDataLayer;
private Map<String, String> fieldToLabelMap;
private boolean disposed;
private ColumnHeaderLayerStack<T> columnHeaderLayer;
private SpaceDef spaceDef;
@Override
public void init(IEditorSite site, final IEditorInput input)
throws PartInitException {
if (!(input instanceof SpaceEditorInput))
throw new PartInitException("Invalid Input: Must be BrowserInput");
setSite(site);
internalInit((SpaceEditorInput) input);
}
public SpaceEditorInput getBrowserInput() {
return (SpaceEditorInput) getEditorInput();
}
public SelectionLayer getSelectionLayer() {
return bodyLayer.getSelectionLayer();
}
public void activate() {
getEditorSite().getPage().activate(this);
}
private void internalInit(final SpaceEditorInput input)
throws PartInitException {
IRunnableWithProgress runnable = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor)
throws InvocationTargetException {
try {
doSetInput(input);
} catch (InterruptedException e) {
throw new InvocationTargetException(e);
}
}
};
try {
getSite().getWorkbenchWindow().run(false, true, runnable);
} catch (InterruptedException x) {
// do nothing
} catch (InvocationTargetException x) {
Throwable t = x.getTargetException();
if (t instanceof CoreException) {
CoreException e = (CoreException) t;
IStatus status = e.getStatus();
if (status.getException() != null)
throw new PartInitException(status);
throw new PartInitException(new Status(status.getSeverity(),
status.getPlugin(), status.getCode(),
status.getMessage(), t));
}
throw new PartInitException(new Status(IStatus.ERROR,
SpaceBarPlugin.ID_PLUGIN, IStatus.OK,
"Space browser could not be initialized.", t));
}
}
private void stopBrowseJob() {
if (browseJob != null) {
browseJob.cancel();
}
}
protected void doSetInput(SpaceEditorInput input)
throws InterruptedException {
if (input == null) {
close(isSaveOnCloseNeeded());
} else {
setInput(input);
Space space = input.getSpace();
setPartName(space.getName());
spaceDef = space.getSpaceDef();
fieldDefs = Utils.getFieldDefs(spaceDef);
fieldNames = Utils.getFieldNames(spaceDef);
nonKeyFieldNames = Utils.getNonKeyFieldNames(spaceDef);
fieldToLabelMap = new HashMap<String, String>();
for (FieldDef fieldDef : fieldDefs) {
fieldToLabelMap.put(fieldDef.getName(), fieldDef.getName());
}
bodyDataProvider = new GlazedListsDataProvider<T>(sortedList,
getPropertyAccessor());
bodyDataLayer = new DataLayer(bodyDataProvider);
glazedListsEventLayer = new GlazedListsEventLayer<T>(bodyDataLayer,
sortedList);
refresh();
}
}
public void refresh() throws InterruptedException {
if (browseJob != null && browseJob.getState() == Job.RUNNING) {
browseJob.cancel();
browseJob.join();
}
eventList.clear();
SpaceEditorInput input = getBrowserInput();
Space space = input.getSpace();
Metaspace metaspace = space.getParent().getParent().getConnection()
.getMetaspace();
AbstractExporter<T> exporter = getExporter(metaspace);
exporter.addTransfer(input.getExport());
exporter.setOutputStream(this);
String jobName = NLS.bind("Browsing space ''{0}''", space);
String task = "Browsing";
String error = "Could not browse";
browseJob = new MetaspaceTransferJob(jobName, task, exporter, error);
browseJob.schedule();
}
protected abstract AbstractExporter<T> getExporter(Metaspace metaspace);
protected String[] getFieldNames() {
return fieldNames;
}
public EventList<T> getEventList() {
return eventList;
}
public SortedList<T> getSortedList() {
return sortedList;
}
protected String[] getNonKeyFieldNames() {
return nonKeyFieldNames;
}
protected ViewportLayer getViewportLayer() {
return bodyLayer.getViewportLayer();
}
protected abstract AbstractConfiguration getConfiguration();
private void close(final boolean save) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (isDisposed()) {
return;
}
getSite().getPage().closeEditor(AbstractBrowser.this, save);
}
});
}
@Override
public void createPartControl(Composite parent) {
IConfigRegistry registry = new ConfigRegistry();
layer = getLayer(glazedListsEventLayer, bodyDataProvider, registry);
ColumnGroupModel columnGroupModel = new ColumnGroupModel();
bodyLayer = new BodyLayerStack(layer, columnGroupModel);
ColumnOverrideLabelAccumulator columnLabelAccumulator = new ColumnOverrideLabelAccumulator(
bodyDataLayer);
bodyDataLayer.setConfigLabelAccumulator(columnLabelAccumulator);
AbstractConfiguration configuration = getConfiguration();
for (int index = 0; index < fieldDefs.length; index++) {
columnLabelAccumulator.registerColumnOverrides(index,
configuration.getLabels(fieldDefs[index]));
}
columnHeaderLayer = new ColumnHeaderLayerStack<T>(sortedList,
filterList, fieldNames, fieldToLabelMap, bodyLayer,
bodyLayer.getSelectionLayer(), columnGroupModel, registry,
getPropertyAccessor());
DisplayColumnChooserCommandHandler columnChooserCommandHandler = new DisplayColumnChooserCommandHandler(
bodyLayer.getSelectionLayer(),
bodyLayer.getColumnHideShowLayer(),
columnHeaderLayer.getColumnHeaderLayer(),
columnHeaderLayer.getColumnHeaderDataLayer(),
columnHeaderLayer.getColumnGroupHeaderLayer(), columnGroupModel);
bodyLayer.registerCommandHandler(columnChooserCommandHandler);
CopyDataCommandHandler copyDataCommandHandler = getCopyDataCommandHandler();
copyDataCommandHandler.setCopyFormattedText(true);
bodyLayer.registerCommandHandler(copyDataCommandHandler);
DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(
bodyDataProvider);
DefaultRowHeaderDataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(
rowHeaderDataProvider);
RowHeaderLayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer,
bodyLayer, bodyLayer.getSelectionLayer());
DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
columnHeaderLayer.getColumnHeaderDataProvider(),
rowHeaderDataProvider);
DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer,
columnHeaderLayer);
GridLayer layer = new GridLayer(bodyLayer, columnHeaderLayer,
rowHeaderLayer, cornerLayer);
natTable = createNatTable(parent, layer, registry);
natTable.configure();
natTable.setTheme(getTheme());
copyDataAction = new CopyDataAction();
}
private CopyDataCommandHandler getCopyDataCommandHandler() {
if (Boolean.TRUE.equals(Preferences
.getBoolean(Preferences.SPACE_EDITOR_CLIPBOARD_HEADER))) {
return new CopyDataCommandHandler(bodyLayer.getSelectionLayer(),
columnHeaderLayer.getColumnHeaderDataLayer(), null);
}
return new CopyDataCommandHandler(bodyLayer.getSelectionLayer());
}
private ThemeConfiguration getTheme() {
String theme = Preferences.getString(Preferences.SPACE_EDITOR_THEME);
if (Preferences.THEME_MODERN.equals(theme)) {
return new ModernNatTableThemeConfiguration();
}
if (Preferences.THEME_DARK.equals(theme)) {
return new DarkNatTableThemeConfiguration();
}
return new DefaultNatTableThemeConfiguration();
}
protected abstract IUniqueIndexLayer getLayer(
GlazedListsEventLayer<T> glazedListsEventLayer,
GlazedListsDataProvider<T> bodyDataProvider,
IConfigRegistry registry);
protected NatTable createNatTable(Composite parent, GridLayer layer,
IConfigRegistry registry) {
NatTable natTable = new NatTable(parent, layer, false);
natTable.setConfigRegistry(registry);
natTable.addConfiguration(new AbstractUiBindingConfiguration() {
@Override
public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
// Mouse move - Show resize cursor
uiBindingRegistry.registerFirstMouseMoveBinding(
new ColumnResizeEventMatcher(SWT.NONE,
GridRegion.ROW_HEADER, 0),
new ColumnResizeCursorAction());
uiBindingRegistry.registerMouseMoveBinding(
new MouseEventMatcher(), new ClearCursorAction());
// Column resize
uiBindingRegistry.registerFirstMouseDragMode(
new ColumnResizeEventMatcher(SWT.NONE,
GridRegion.ROW_HEADER, 1),
new ColumnResizeDragMode());
uiBindingRegistry.registerDoubleClickBinding(
new ColumnResizeEventMatcher(SWT.NONE,
GridRegion.ROW_HEADER, 1),
new AutoResizeColumnAction());
uiBindingRegistry.registerSingleClickBinding(
new ColumnResizeEventMatcher(SWT.NONE,
GridRegion.ROW_HEADER, 1),
new NoOpMouseAction());
}
});
natTable.addConfiguration(getHeaderMenuConfiguration(natTable));
natTable.addConfiguration(new SingleClickSortConfiguration());
natTable.addConfiguration(new FilterRowConfiguration(fieldDefs));
natTable.addConfiguration(getConfiguration());
natTable.getUiBindingRegistry().registerKeyBinding(
new KeyEventMatcher(SWT.MOD1, 'c'), copyDataAction);
new NatTableContentTooltip(natTable, GridRegion.BODY);
return natTable;
}
protected IConfiguration getHeaderMenuConfiguration(NatTable natTable) {
return new DefaultHeaderMenuConfiguration(natTable);
}
protected abstract IColumnPropertyAccessor<T> getPropertyAccessor();
@Override
public void dispose() {
this.disposed = true;
stopBrowseJob();
if (natTable != null) {
natTable.dispose();
natTable = null;
}
if (bodyLayer != null) {
bodyLayer.dispose();
bodyLayer = null;
}
if (layer != null) {
layer.dispose();
layer = null;
}
if (bodyDataLayer != null) {
bodyDataLayer.dispose();
bodyDataLayer = null;
}
if (glazedListsEventLayer != null) {
glazedListsEventLayer.dispose();
glazedListsEventLayer = null;
}
if (filterList != null) {
filterList.dispose();
filterList = null;
}
if (sortedList != null) {
sortedList.dispose();
sortedList = null;
}
if (eventList != null) {
eventList.dispose();
eventList = null;
}
if (columnHeaderLayer != null) {
columnHeaderLayer.dispose();
columnHeaderLayer = null;
}
super.dispose();
}
public boolean isDisposed() {
return disposed;
}
@Override
public void doSave(IProgressMonitor monitor) {
}
@Override
public void doSaveAs() {
// not supported
}
@Override
public boolean isDirty() {
return false;
}
@Override
public boolean isSaveAsAllowed() {
return false;
}
@Override
public void setFocus() {
// do nothing
}
public Space getSpace() {
return getBrowserInput().getSpace();
}
public void copy(Event event) {
copyDataAction.run(natTable, new KeyEvent(event));
}
protected NatTable getNatTable() {
return natTable;
}
protected PropertyChangeListener getPropertyChangeListener() {
return glazedListsEventLayer;
}
protected GlazedListsEventLayer<T> getGlazedListsEventLayer() {
return glazedListsEventLayer;
}
@Override
public void open() throws Exception {
}
@Override
public void write(List<T> elements) {
if (isDisposed()) {
return;
}
Lock lock = eventList.getReadWriteLock().writeLock();
lock.lock();
try {
eventList.addAll(elements);
} finally {
lock.unlock();
}
}
@Override
public void write(T element) {
if (isDisposed()) {
return;
}
Lock lock = eventList.getReadWriteLock().writeLock();
lock.lock();
try {
eventList.add(element);
} finally {
lock.unlock();
}
}
@Override
public void close() {
}
@Override
public boolean isClosed() {
return isDisposed();
}
}