/*******************************************************************************
* Copyright (c) 2010, 2011 LogSaw project and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* LogSaw project committers - initial API and implementation
*******************************************************************************/
package net.sf.logsaw.ui.editors;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import net.sf.logsaw.core.field.ALogEntryField;
import net.sf.logsaw.core.field.ILogEntryFieldVisitor;
import net.sf.logsaw.core.field.Level;
import net.sf.logsaw.core.field.LogEntry;
import net.sf.logsaw.core.field.model.DateLogEntryField;
import net.sf.logsaw.core.field.model.LevelLogEntryField;
import net.sf.logsaw.core.field.model.StringLogEntryField;
import net.sf.logsaw.core.logresource.ILogResource;
import net.sf.logsaw.core.query.IRestrictable;
import net.sf.logsaw.index.IQueryContext;
import net.sf.logsaw.index.IndexPlugin;
import net.sf.logsaw.index.ResultPage;
import net.sf.logsaw.ui.IHelpContexts;
import net.sf.logsaw.ui.Messages;
import net.sf.logsaw.ui.UIPlugin;
import net.sf.logsaw.ui.viewers.LogEntryTableLabelProvider;
import org.eclipse.core.runtime.Assert;
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.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.PageChangedEvent;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerEditor;
import org.eclipse.jface.viewers.TableViewerFocusCellManager;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.statushandlers.StatusManager;
/**
* @author Philipp Nanz
*/
public class LogViewEditor extends EditorPart implements ILogViewEditor {
/**
* The ID of the view as specified by the extension.
*/
public static final String ID = "net.sf.logsaw.ui.editors.LogViewEditor"; //$NON-NLS-1$
private TableViewer viewer;
private TableViewerFocusCellManager focusCellManager;
private LogEntryTableLabelProvider labelProvider;
private Label resultLabel;
private ResultPage currentPage;
private LogViewEditorColumnConfiguration columnConfig;
private List<IPageChangedListener> listeners =
new ArrayList<IPageChangedListener>();
private int currentPageNumber;
private int pageSize = 1000;
private IEditorInput editorInput;
private IQueryContext queryContext;
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createPartControl(Composite parent) {
Composite root = new Composite(parent, SWT.BORDER);
GridLayout gridLayout = new GridLayout();
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
gridLayout.verticalSpacing = 0;
root.setLayout(gridLayout);
Composite top = new Composite(root, SWT.NONE);
top.setLayout(new GridLayout());
top.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
resultLabel = new Label(top, SWT.NONE);
resultLabel.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
Label label = new Label(root, SWT.SEPARATOR | SWT.HORIZONTAL);
label.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
viewer = new TableViewer(root, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
viewer.getTable().setHeaderVisible(true);
viewer.getTable().setLinesVisible(true);
labelProvider = new LogEntryTableLabelProvider();
labelProvider.setLog(getLogResource());
viewer.setLabelProvider(labelProvider);
viewer.setContentProvider(new ArrayContentProvider());
getSite().setSelectionProvider(viewer);
// Enable table cell navigation
focusCellManager =
new TableViewerFocusCellManager(viewer, new FocusCellOwnerDrawHighlighter(viewer));
ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(viewer) {
@Override
protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
return (event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL)
|| (event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION)
|| ((event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED) && (event.keyCode == SWT.CR))
|| (event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC);
}
};
TableViewerEditor.create(viewer, focusCellManager, actSupport, ColumnViewerEditor.TABBING_HORIZONTAL
| ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
| ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);
// Setup popup menu
hookContextMenu();
// Setup columns
setColumnConfig(new LogViewEditorColumnConfiguration(getLogResource()));
goToPage(1);
// Enable dynamic help
PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(),
IHelpContexts.LOG_VIEWER);
}
private void hookContextMenu() {
MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager manager) {
// Other plug-ins can contribute their actions here
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
});
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
getSite().registerContextMenu(menuMgr, viewer);
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#clearQueryContext()
*/
@Override
public synchronized void clearQueryContext() {
queryContext = null;
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#setColumnConfig(net.sf.logsaw.ui.editors.LogViewEditorColumnConfiguration)
*/
@Override
public void setColumnConfig(LogViewEditorColumnConfiguration config) {
Assert.isNotNull(config, "config"); //$NON-NLS-1$
columnConfig = config;
updateColumns(config.getFields(), config.getWidths());
}
private void updateColumns(List<ALogEntryField<?, ?>> newFields, int[] newWidths) {
// Prevent flickering
viewer.getTable().setRedraw(false);
labelProvider.setFields(newFields.toArray(new ALogEntryField[newFields.size()]));
// Dispose old columns
TableColumn[] oldColumns = viewer.getTable().getColumns();
for (int i = 0; i < oldColumns.length; i++) {
oldColumns[i].dispose();
}
// Create icon column
TableColumn col = new TableColumn(viewer.getTable(), SWT.NONE);
col.setWidth(20);
col.setResizable(false);
int i = 0;
for (ALogEntryField<?, ?> newField : newFields) {
col = new TableColumn(viewer.getTable(), SWT.NONE);
col.setText(newField.getLabel());
// Set order mark
if (newField.equals(getLogResource().getDialect().getFieldProvider().getTimestampField())) {
viewer.getTable().setSortColumn(col);
viewer.getTable().setSortDirection(SWT.UP);
}
// Set width
int newWidth = newWidths[i++];
if (newWidth > 0) {
col.setWidth(newWidth);
} else {
col.setWidth(100);
}
col.addListener(SWT.Resize, new Listener() {
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
@Override
public void handleEvent(Event event) {
saveColumnConfig();
}
});
}
viewer.refresh(true);
viewer.getTable().setRedraw(true);
}
private void saveColumnConfig() {
// Save column widths
TableColumn[] columns = viewer.getTable().getColumns();
int[] newWidths = new int[columns.length - 1];
int i = 0;
boolean first = true;
for (TableColumn col : columns) {
if (first) {
// Ignore icon column
first = false;
continue;
}
newWidths[i++] = col.getWidth();
}
columnConfig.update(columnConfig.getFields(), newWidths);
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#previousPage()
*/
@Override
public void previousPage() {
Assert.isNotNull(currentPage, "Current page must not be null"); //$NON-NLS-1$
Assert.isTrue(isPreviousPageAllowed(), "Current page number must be greater than 1"); //$NON-NLS-1$
goToPage(currentPageNumber - 1);
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#nextPage()
*/
@Override
public void nextPage() {
Assert.isNotNull(currentPage, "Current page must not be null"); //$NON-NLS-1$
Assert.isTrue(isNextPageAllowed(), "There must exist more items to display"); //$NON-NLS-1$
goToPage(currentPageNumber + 1);
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#isPreviousPageAllowed()
*/
@Override
public boolean isPreviousPageAllowed() {
return currentPageNumber > 1;
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#isNextPageAllowed()
*/
@Override
public boolean isNextPageAllowed() {
return (currentPage != null) && (currentPage.getTotalHits() > (currentPageNumber * pageSize));
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#getFocusCellLogEntryField()
*/
@Override
public ALogEntryField<?, ?> getFocusCellLogEntryField() {
ViewerCell cell = focusCellManager.getFocusCell();
if ((cell != null) && (cell.getColumnIndex() > 0)) {
return columnConfig.getFields().get(cell.getColumnIndex() - 1);
}
return null;
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#getFocusCellText()
*/
@Override
public String getFocusCellText() {
ALogEntryField<?, ?> fld = getFocusCellLogEntryField();
final LogEntry entry = getSelectedLogEntry();
if ((fld == null) || (entry == null)) {
return ""; //$NON-NLS-1$
}
final String[] ret = new String[1];
// Setup visitor
ILogEntryFieldVisitor visitor = new ILogEntryFieldVisitor() {
/* (non-Javadoc)
* @see net.sf.logsaw.core.model.ILogEntryFieldVisitor#visit(net.sf.logsaw.core.model.StringLogEntryField)
*/
@Override
public void visit(StringLogEntryField fld) {
String value = entry.get(fld);
if (value != null) {
ret[0] = fld.toInputValue(value, getLogResource());
}
}
/* (non-Javadoc)
* @see net.sf.logsaw.core.model.ILogEntryFieldVisitor#visit(net.sf.logsaw.core.model.LevelLogEntryField)
*/
@Override
public void visit(LevelLogEntryField fld) {
Level value = entry.get(fld);
if (value != null) {
ret[0] = fld.toInputValue(value, getLogResource());
}
}
/* (non-Javadoc)
* @see net.sf.logsaw.core.model.ILogEntryFieldVisitor#visit(net.sf.logsaw.core.model.DateLogEntryField)
*/
@Override
public void visit(DateLogEntryField fld) {
Date value = entry.get(fld);
if (value != null) {
ret[0] = fld.toInputValue(value, getLogResource());
}
}
};
fld.visit(visitor);
if ((ret[0] == null) || (ret[0].length() == 0)) {
return ""; //$NON-NLS-1$
}
return ret[0];
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#getSelectedLogEntry()
*/
@Override
public LogEntry getSelectedLogEntry() {
ViewerCell cell = focusCellManager.getFocusCell();
if ((cell != null) && (cell.getColumnIndex() > 0)) {
return (LogEntry) cell.getElement();
}
return null;
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.parts.IRefreshablePart#refresh()
*/
@Override
public void refresh() {
goToPage(((Integer) getSelectedPage()).intValue());
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#goToPage(int)
*/
@Override
public void goToPage(int pageNumber) {
// Sanity checks
if (pageNumber < 1) {
pageNumber = 1;
}
if (currentPage == null) {
Assert.isTrue(pageNumber == 1, "pageNumber must initially be set to 1"); //$NON-NLS-1$
} else {
// Fallback to last page if page does not exist
int pageCount = getPageCount();
if (pageNumber > pageCount) {
pageNumber = pageCount;
}
}
// Obtain fresh query context - if necessary
synchronized (this) {
if (queryContext == null) {
queryContext = IndexPlugin.getDefault().getIndexService().createQueryContext(
getLogResource());
}
}
try {
currentPage = IndexPlugin.getDefault().getIndexService().query(queryContext,
getRestrictable().getRestrictions(), (pageNumber - 1) * pageSize, pageSize);
currentPageNumber = pageNumber;
NumberFormat fmt = DecimalFormat.getInstance();
String header = currentPage.getTotalHits() == 0 ?
Messages.LogViewEditor_pageHeader_empty :
NLS.bind(Messages.LogViewEditor_pageHeader,
new Object[] {fmt.format(currentPage.getOffset() + 1),
fmt.format(currentPage.getOffset() + currentPage.getItems().size()),
fmt.format(currentPage.getTotalHits())});
if (!getRestrictable().getRestrictions().isEmpty()) {
// Append suffix to show that filter is active
header += Messages.LogViewEditor_pageHeader_filterSuffix;
}
resultLabel.setText(header);
viewer.setInput(currentPage.getItems().toArray());
firePageChanged();
} catch (CoreException e) {
// Log and show error
UIPlugin.logAndShowError(e, false);
}
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#getPageCount()
*/
@Override
public int getPageCount() {
if (currentPage == null) {
// Avoid NPE
return 1;
}
return (int) Math.max(Math.ceil(currentPage.getTotalHits() / (double) pageSize), 1);
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.IPageChangeProvider#getSelectedPage()
*/
@Override
public Object getSelectedPage() {
return Integer.valueOf(currentPageNumber);
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.IPageChangeProvider#addPageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
*/
@Override
public void addPageChangedListener(IPageChangedListener listener) {
listeners.add(listener);
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.IPageChangeProvider#removePageChangedListener(org.eclipse.jface.dialogs.IPageChangedListener)
*/
@Override
public void removePageChangedListener(IPageChangedListener listener) {
listeners.remove(listener);
}
private void firePageChanged() {
PageChangedEvent e = new PageChangedEvent(this, getSelectedPage());
for (IPageChangedListener listener : listeners) {
listener.pageChanged(e);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void doSave(IProgressMonitor monitor) {
// n/a
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#doSaveAs()
*/
@Override
public void doSaveAs() {
// n/a
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)
*/
@Override
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
setPartName(input.getName());
setSite(site);
setInput(input);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#setInput(org.eclipse.ui.IEditorInput)
*/
@Override
protected void setInput(IEditorInput input) {
editorInput = input;
super.setInput(input);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#getEditorInput()
*/
@Override
public IEditorInput getEditorInput() {
// Return a fresh instance of IEditorInput
return (IEditorInput) getLogResource().getAdapter(IEditorInput.class);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#dispose()
*/
@Override
public void dispose() {
super.dispose();
if (queryContext != null) {
try {
queryContext.close();
} catch (IOException e) {
// Log error
StatusManager.getManager().handle(
new Status(IStatus.ERROR, UIPlugin.PLUGIN_ID,
e.getLocalizedMessage(), e), StatusManager.LOG);
}
}
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#getLogResource()
*/
@Override
public ILogResource getLogResource() {
return (ILogResource) editorInput.getAdapter(ILogResource.class);
}
/* (non-Javadoc)
* @see net.sf.logsaw.ui.editors.ILogViewEditor#getRestrictable()
*/
@Override
public IRestrictable getRestrictable() {
return (IRestrictable) editorInput.getAdapter(IRestrictable.class);
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#isDirty()
*/
@Override
public boolean isDirty() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
*/
@Override
public boolean isSaveAsAllowed() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#setFocus()
*/
@Override
public void setFocus() {
viewer.getControl().setFocus();
}
}