/******************************************************************************
* Copyright (c) 2007 g-Eclipse consortium
* 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
*
* Initial development of the original code was made for
* project g-Eclipse founded by European Union
* project number: FP6-IST-034327 http://www.geclipse.eu/
*
* Contributor(s):
* PSNC:
* - Katarzyna Bylec (katis@man.poznan.pl)
*
*****************************************************************************/
package eu.geclipse.servicejob.ui.views;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Region;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IPersistableElement;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.ViewPart;
import eu.geclipse.core.model.GridModel;
import eu.geclipse.core.model.IGridElement;
import eu.geclipse.core.model.IGridModelEvent;
import eu.geclipse.core.model.IGridModelListener;
import eu.geclipse.core.model.IServiceJob;
import eu.geclipse.core.model.IServiceJobResult;
import eu.geclipse.servicejob.ui.Activator;
import eu.geclipse.servicejob.ui.providers.DetailsContentProvider;
import eu.geclipse.servicejob.ui.providers.DetailsLabelProvider;
/**
* View that is called "Operator's Job History". It displays detailed information
* about single service job, such as: list of resources on which this service
* job was run, sub-jobs list and results for each sub-job and each resource.
*/
public class ServiceJobDetailsView extends ViewPart
implements ISelectionListener, SelectionListener, IGridModelListener
{
/**
* Name of folder (in state location of this plug-in) where service jobs's
* result data is being serialized to files when needed (e.g. when this data
* can be opened only with external - system - editor).
*/
public static final String SERVICE_JOB_FILES_FOLDER = "./serviceJobFiles"; //$NON-NLS-1$
protected Point pt = new Point( 0, 0 );
int selectedColumnIndex;
String selectedColumnText;
TreeViewer viewer;
IServiceJob rootElement;
private DetailsContentProvider contentProvider;
private DetailsLabelProvider labelProvider;
private Composite parent;
/**
* Constructs instance of this view.
*/
public ServiceJobDetailsView() {
GridModel.addGridModelListener( this );
this.contentProvider = new DetailsContentProvider( null );
this.labelProvider = new DetailsLabelProvider();
}
@Override
public void createPartControl( final Composite parent1 ) {
this.parent = parent1;
this.rootElement = findSelectedServiceJob();
if( this.rootElement != null ) {
updateViewer( this.rootElement );
} else {
Composite mainComp = new Composite( parent1, SWT.NONE );
mainComp.setLayout( new GridLayout( 1, false ) );
Label label = new Label( mainComp, SWT.LEAD );
label.setText( Messages.getString( "ServiceJobsDetailsView.no_service_job_selected" ) ); //$NON-NLS-1$
label.setLayoutData( new GridData() );
}
getSite().getPage().addSelectionListener( this );
}
private void updateViewer( final IServiceJob newInput ) {
createViewer( this.parent );
List<TreeColumn> columns = new ArrayList<TreeColumn>();
TreeColumn nameColumn = new TreeColumn( this.viewer.getTree(), SWT.NONE );
nameColumn.setText( "Resource" ); //$NON-NLS-1$
nameColumn.setAlignment( SWT.LEFT );
nameColumn.setWidth( 200 );
if( newInput != null ) {
String[] properties = new String[ newInput.getSingleServiceJobNames().size() + 1 ];
properties[ 0 ] = Messages.getString( "ServiceJobsDetailsView.name" ); //$NON-NLS-1$
int i = 0;
for( String subServiceJob : newInput.getSingleServiceJobNames() ) {
columns.add( new TreeColumn( this.viewer.getTree(), SWT.NONE ) );
columns.get( i ).setText( subServiceJob );
columns.get( i ).setAlignment( SWT.LEFT );
columns.get( i ).setWidth( newInput.getColumnWidth( subServiceJob ) );
properties[ i ] = subServiceJob;
i++;
}
}
( ( DetailsContentProvider )this.viewer.getContentProvider() ).changeRoot( newInput );
( ( DetailsLabelProvider )this.viewer.getLabelProvider() ).changeRoot( newInput );
this.viewer.setInput( newInput );
this.viewer.refresh();
this.parent.redraw();
this.parent.update();
addSelectionListenerForEditor();
this.parent.layout();
}
private void addSelectionListenerForEditor() {
this.viewer.addDoubleClickListener( new IDoubleClickListener() {
@SuppressWarnings("unchecked")
public void doubleClick( final DoubleClickEvent event ) {
ISelection sel = event.getSelection();
if( sel instanceof TreeSelection ) {
TreeSelection structured = ( TreeSelection )sel;
if( structured.getFirstElement() instanceof List
&& ServiceJobDetailsView.this.selectedColumnIndex != 0 )
{
List<IServiceJobResult> list = ( List<IServiceJobResult> )structured.getFirstElement();
for( IServiceJobResult result : list ) {
if( result.getSubServiceJobName()
.equals( ServiceJobDetailsView.this.selectedColumnText )
&& ServiceJobDetailsView.this.rootElement.getInputStreamForResult( result ) != null )
{
String extension = result.getResultType();
if( extension == null || extension.equals( "" ) ) { //$NON-NLS-1$
extension = "txt"; //$NON-NLS-1$
}
IWorkbenchWindow window = PlatformUI.getWorkbench()
.getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
if( page != null ) {
try {
IEditorDescriptor desc = PlatformUI.getWorkbench()
.getEditorRegistry()
.getDefaultEditor( "name." + extension ); //$NON-NLS-1$
if( desc != null ) {
IStorage storage = new StreamStorage( ServiceJobDetailsView.this.rootElement.getInputStreamForResult( result ),
result.getSubServiceJobName()
+ " - " //$NON-NLS-1$
+ result.getResourceName() );
IStorageEditorInput editorInput = new StreamInput( storage,
result );
page.openEditor( editorInput, desc.getId() );
} else {
File fileForEditor = null;
if( ( fileForEditor = Activator.getFileForServiceJobResult( result ) ) == null )
{
fileForEditor = serializeInputStream( ServiceJobDetailsView.this.rootElement.getInputStreamForResult( result ),
extension );
Activator.addFileForServiceJobResult( result, fileForEditor );
}
IFileStore fileStore = EFS.getLocalFileSystem()
.getStore( fileForEditor.toURI() );
IDE.openEditorOnFileStore( page, fileStore );
}
} catch( PartInitException e ) {
// TODO katis
Activator.logException( e );
}
}
break;
}
}
}
}
}
} );
this.viewer.getTree().addListener( SWT.EraseItem, new Listener() {
public void handleEvent( final Event event ) {
event.detail &= ~SWT.HOT;
if( ( event.detail & SWT.SELECTED ) != 0 ) {
GC gc = event.gc;
Rectangle area = ServiceJobDetailsView.this.viewer.getTree()
.getClientArea();
int columnCount = ServiceJobDetailsView.this.viewer.getTree()
.getColumnCount();
if( event.index == columnCount - 1 || columnCount == 0 ) {
int width = area.x + area.width - event.x;
if( width > 0 ) {
Region region = new Region();
gc.getClipping( region );
region.add( event.x, event.y, width, event.height );
gc.setClipping( region );
region.dispose();
}
}
gc.setAdvanced( true );
if( gc.getAdvanced() ) {
gc.setAlpha( 127 );
}
Rectangle rect = event.getBounds();
TreeItem item = ServiceJobDetailsView.this.viewer.getTree()
.getItem( ServiceJobDetailsView.this.pt );
for( int i = 0; i < ServiceJobDetailsView.this.viewer.getTree()
.getColumnCount(); i++ )
{
if( item != null ) {
Rectangle rect1 = item.getBounds( i );
if( rect1.contains( ServiceJobDetailsView.this.pt ) ) {
rect = rect1;
}
}
}
gc.setForeground( Display.getCurrent()
.getSystemColor( SWT.COLOR_LIST_SELECTION ) );
gc.setBackground( Display.getCurrent()
.getSystemColor( SWT.COLOR_LIST_SELECTION ) );
gc.fillGradientRectangle( rect.x,
rect.y,
rect.width,
rect.height,
false );
gc.setForeground( Display.getCurrent()
.getSystemColor( SWT.COLOR_LIST_FOREGROUND ) );
gc.setBackground( Display.getCurrent()
.getSystemColor( SWT.COLOR_LIST_BACKGROUND ) );
event.detail &= ~SWT.SELECTED;
}
}
} );
}
class StringStorage extends PlatformObject implements IStorage {
private String string;
StringStorage( final String input ) {
this.string = input;
}
public InputStream getContents() throws CoreException {
return new ByteArrayInputStream( this.string.getBytes() );
}
public IPath getFullPath() {
return null;
}
public String getName() {
int len = Math.min( 5, this.string.length() );
return this.string.substring( 0, len ).concat( "..." ); //$NON-NLS-1$
}
public boolean isReadOnly() {
return true;
}
}
class StringInput extends PlatformObject implements IStorageEditorInput {
private IStorage storage;
StringInput( final IStorage storage ) {
this.storage = storage;
}
public boolean exists() {
return true;
}
public ImageDescriptor getImageDescriptor() {
return null;
}
public String getName() {
return this.storage.getName();
}
public IPersistableElement getPersistable() {
return null;
}
public IStorage getStorage() {
return this.storage;
}
public String getToolTipText() {
return Messages.getString( "ServiceJobsDetailsView.string_based_file" ) + ": " + this.storage.getName(); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private void createViewer( final Composite parent1 ) {
TreeViewer tViewer = null;
for( Control comp : parent1.getChildren() ) {
comp.dispose();
}
tViewer = new TreeViewer( parent1, SWT.VIRTUAL
| SWT.MULTI
| SWT.H_SCROLL
| SWT.V_SCROLL
| SWT.FULL_SELECTION );
Tree tree = tViewer.getTree();
tree.setHeaderVisible( true );
this.viewer = tViewer;
this.viewer.setContentProvider( this.contentProvider );
this.viewer.setLabelProvider( this.labelProvider );
this.viewer.getTree().addListener( SWT.MouseDoubleClick, new Listener() {
public void handleEvent( final Event event ) {
Point point = new Point( event.x, event.y );
TreeItem item = ServiceJobDetailsView.this.viewer.getTree().getItem( point );
if( item != null ) {
for( int i = 0; i < ServiceJobDetailsView.this.viewer.getTree()
.getColumnCount(); i++ )
{
Rectangle rect = item.getBounds( i );
if( rect.contains( point ) ) {
if( !ServiceJobDetailsView.this.viewer.getTree()
.getItem( point )
.getText( i )
.equals( "" ) ) //$NON-NLS-1$
{
ServiceJobDetailsView.this.selectedColumnIndex = i;
ServiceJobDetailsView.this.selectedColumnText = ServiceJobDetailsView.this.viewer.getTree()
.getColumn( i )
.getText();
break;
} else {
// TODO katis
// do nothing
}
} else {
ServiceJobDetailsView.this.selectedColumnText = ""; //$NON-NLS-1$
ServiceJobDetailsView.this.selectedColumnIndex = -1;
}
}
}
}
} );
}
File serializeInputStream( final InputStream inputStream, final String type )
{
File res = null;
File serviceJobFolder = Activator.getDefault()
.getStateLocation()
.append( SERVICE_JOB_FILES_FOLDER )
.toFile();
if( !serviceJobFolder.exists() ) {
serviceJobFolder.mkdir();
}
boolean fileCreated = false;
String fileNameBase = "file"; //$NON-NLS-1$
String fileNameSufix = ""; //$NON-NLS-1$
int i = 0;
while( !fileCreated ) {
String fileName = fileNameBase + fileNameSufix + "." + type; //$NON-NLS-1$
Path filePath = new Path( serviceJobFolder.getAbsolutePath() );
if( filePath.append( fileName ).toFile().exists() ) {
fileNameSufix = Integer.toString( i );
i++;
if( i == 100 ) {
break;
}
} else {
DataOutputStream out = null;
try {
File newFile = filePath.append( fileName ).toFile();
fileCreated = true;
if( newFile.createNewFile() ) {
out = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( newFile ) ) );
int c;
while( ( c = inputStream.read() ) != -1 ) {
out.writeByte( c );
}
res = newFile;
}
} catch( IOException e ) {
// TODO Auto-generated catch block
Activator.logException( e );
} finally {
try {
inputStream.close();
if( out != null ) {
out.close();
}
} catch( IOException e ) {
//Ignore
}
}
}
}
return res;
}
protected IServiceJob findSelectedServiceJob() {
IServiceJob result = null;
ISelection selection = getSite().getPage().getSelection();
if( selection != null && selection instanceof IStructuredSelection ) {
Object obj = ( ( IStructuredSelection )selection ).getFirstElement();
if( obj != null && obj instanceof IServiceJob ) {
result = ( IServiceJob )obj;
}
}
return result;
}
@Override
public void setFocus() {
// TODO Auto-generated method stub
}
public void selectionChanged( final IWorkbenchPart part,
final ISelection selection )
{
if( selection instanceof IStructuredSelection ) {
Object obj = ( ( IStructuredSelection )selection ).getFirstElement();
if( obj != null && obj instanceof IServiceJob ) {
this.rootElement = ( IServiceJob )obj;
updateViewer( ( IServiceJob )obj );
this.viewer.getTree().update();
this.viewer.getTree().redraw();
this.viewer.getTree().layout();
this.parent.redraw();
this.parent.update();
this.parent.layout();
}
}
}
@Override
public void dispose() {
getSite().getPage().removeSelectionListener( this );
super.dispose();
}
public void widgetDefaultSelected( final SelectionEvent e ) {
// empty
}
public void widgetSelected( final SelectionEvent e ) {
// empty
}
public void gridModelChanged( final IGridModelEvent event ) {
if( event.getType() == IGridModelEvent.ELEMENTS_CHANGED ) {
IGridElement[] removedElements = event.getElements();
if( this.viewer != null ) {
Control control = this.viewer.getControl();
for( IGridElement elem : removedElements ) {
if( elem instanceof IServiceJob ) {
if( !control.isDisposed() ) {
Display display = control.getDisplay();
display.asyncExec( new Runnable() {
public void run() {
ServiceJobDetailsView.this.viewer.refresh( true );
}
} );
}
}
}
}
}
}
class StreamStorage extends PlatformObject implements IStorage {
private InputStream stream;
private String name;
StreamStorage( final InputStream input, final String name ) {
this.stream = input;
this.name = name;
}
public InputStream getContents() throws CoreException {
return this.stream;
}
public IPath getFullPath() {
return null;
}
public String getName() {
return this.name;
}
public boolean isReadOnly() {
return true;
}
}
class StreamInput extends PlatformObject implements IStorageEditorInput {
private IStorage storage;
private IServiceJobResult result;
StreamInput( final IStorage sto, final IServiceJobResult result ) {
this.storage = sto;
this.result = result;
}
public boolean exists() {
return true;
}
public ImageDescriptor getImageDescriptor() {
return null;
}
public String getName() {
return this.storage.getName();
}
public IPersistableElement getPersistable() {
return null;
}
public IStorage getStorage() {
return this.storage;
}
public String getToolTipText() {
return Messages.getString( "ServiceJobsDetailsView.results" ) + ":" + this.storage.getName(); //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
public boolean equals( final Object obj ) {
boolean res = super.equals( obj );
if( !res && obj instanceof StreamInput ) {
res = this.result.equals( ( ( StreamInput )obj ).result );
}
return res;
}
@Override
public int hashCode() {
return this.result.hashCode();
}
}
}