/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.di.ui.spoon.delegates;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.vfs2.FileObject;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.LocationListener;
import org.eclipse.swt.browser.OpenWindowListener;
import org.eclipse.swt.browser.WindowEvent;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.widgets.Composite;
import org.pentaho.di.base.AbstractMeta;
import org.pentaho.di.cluster.SlaveServer;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.EngineMetaInterface;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleFileException;
import org.pentaho.di.core.extension.ExtensionPointHandler;
import org.pentaho.di.core.extension.KettleExtensionPoint;
import org.pentaho.di.core.gui.SpoonInterface;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.repository.ObjectRevision;
import org.pentaho.di.repository.RepositoryOperation;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.ui.core.PropsUI;
import org.pentaho.di.ui.core.gui.GUIResource;
import org.pentaho.di.ui.repository.RepositorySecurityUI;
import org.pentaho.di.ui.spoon.AbstractGraph;
import org.pentaho.di.ui.spoon.Spoon;
import org.pentaho.di.ui.spoon.SpoonBrowser;
import org.pentaho.di.ui.spoon.TabItemInterface;
import org.pentaho.di.ui.spoon.TabMapEntry;
import org.pentaho.di.ui.spoon.TabMapEntry.ObjectType;
import org.pentaho.di.ui.spoon.job.JobGraph;
import org.pentaho.di.ui.spoon.trans.TransGraph;
import org.pentaho.ui.util.Launch;
import org.pentaho.ui.util.Launch.Status;
import org.pentaho.xul.swt.tab.TabItem;
import org.pentaho.xul.swt.tab.TabSet;
public class SpoonTabsDelegate extends SpoonDelegate {
private static Class<?> PKG = Spoon.class; // for i18n purposes, needed by Translator2!!
/**
* This contains a list of the tab map entries
*/
private List<TabMapEntry> tabMap;
public SpoonTabsDelegate( Spoon spoon ) {
super( spoon );
tabMap = new ArrayList<TabMapEntry>();
}
public boolean tabClose( TabItem item ) throws KettleException {
return tabClose( item, false );
}
public boolean tabClose( TabItem item, boolean force ) throws KettleException {
// Try to find the tab-item that's being closed.
boolean createPerms = !RepositorySecurityUI
.verifyOperations( Spoon.getInstance().getShell(), Spoon.getInstance().getRepository(), false,
RepositoryOperation.MODIFY_TRANSFORMATION, RepositoryOperation.MODIFY_JOB );
boolean close = true;
boolean canSave = true;
for ( TabMapEntry entry : tabMap ) {
if ( item.equals( entry.getTabItem() ) ) {
final TabItemInterface itemInterface = entry.getObject();
final Object managedObject = itemInterface.getManagedObject();
if ( !force ) {
if ( managedObject != null
&& AbstractMeta.class.isAssignableFrom( managedObject.getClass() ) ) {
canSave = !( (AbstractMeta) managedObject ).hasMissingPlugins();
}
if ( canSave ) {
// Can we close this tab? Only allow users with create content perms to save
if ( !itemInterface.canBeClosed() && createPerms ) {
int reply = itemInterface.showChangedWarning();
if ( reply == SWT.YES ) {
close = itemInterface.applyChanges();
} else {
if ( reply == SWT.CANCEL ) {
close = false;
} else {
close = true;
}
}
}
}
}
String beforeCloseId = null;
String afterCloseId = null;
if ( itemInterface instanceof TransGraph ) {
beforeCloseId = KettleExtensionPoint.TransBeforeClose.id;
afterCloseId = KettleExtensionPoint.TransAfterClose.id;
} else if ( itemInterface instanceof JobGraph ) {
beforeCloseId = KettleExtensionPoint.JobBeforeClose.id;
afterCloseId = KettleExtensionPoint.JobAfterClose.id;
}
if ( beforeCloseId != null ) {
try {
ExtensionPointHandler.callExtensionPoint( log, beforeCloseId, managedObject );
} catch ( KettleException e ) {
// prevent tab close
close = false;
}
}
// Also clean up the log/history associated with this
// transformation/job
//
if ( close ) {
if ( itemInterface instanceof TransGraph ) {
TransMeta transMeta = (TransMeta) managedObject;
spoon.delegates.trans.closeTransformation( transMeta );
spoon.refreshTree();
// spoon.refreshCoreObjects();
} else if ( itemInterface instanceof JobGraph ) {
JobMeta jobMeta = (JobMeta) managedObject;
spoon.delegates.jobs.closeJob( jobMeta );
spoon.refreshTree();
// spoon.refreshCoreObjects();
} else if ( itemInterface instanceof SpoonBrowser ) {
this.removeTab( entry );
spoon.refreshTree();
} else if ( itemInterface instanceof Composite ) {
Composite comp = (Composite) itemInterface;
if ( comp != null && !comp.isDisposed() ) {
comp.dispose();
}
}
if ( afterCloseId != null ) {
try {
ExtensionPointHandler.callExtensionPoint( log, afterCloseId, managedObject );
} catch ( KettleException e ) {
// fails gracefully... what else could we do?
}
}
}
break;
}
}
return close;
}
public void removeTab( TabMapEntry tabMapEntry ) {
for ( TabMapEntry entry : getTabs() ) {
if ( tabMapEntry.equals( entry ) ) {
tabMap.remove( tabMapEntry );
}
}
if ( !tabMapEntry.getTabItem().isDisposed() ) {
tabMapEntry.getTabItem().dispose();
}
}
public List<TabMapEntry> getTabs() {
List<TabMapEntry> list = new ArrayList<TabMapEntry>();
list.addAll( tabMap );
return list;
}
public TabMapEntry getTab( TabItem tabItem ) {
for ( TabMapEntry tabMapEntry : tabMap ) {
if ( tabMapEntry.getTabItem().equals( tabItem ) ) {
return tabMapEntry;
}
}
return null;
}
public EngineMetaInterface getActiveMeta() {
TabSet tabfolder = spoon.tabfolder;
if ( tabfolder == null ) {
return null;
}
TabItem tabItem = tabfolder.getSelected();
if ( tabItem == null ) {
return null;
}
// What transformation is in the active tab?
// TransLog, TransGraph & TransHist contain the same transformation
//
TabMapEntry mapEntry = getTab( tabfolder.getSelected() );
EngineMetaInterface meta = null;
if ( mapEntry != null ) {
if ( mapEntry.getObject() instanceof TransGraph ) {
meta = ( mapEntry.getObject() ).getMeta();
}
if ( mapEntry.getObject() instanceof JobGraph ) {
meta = ( mapEntry.getObject() ).getMeta();
}
}
return meta;
}
public String makeSlaveTabName( SlaveServer slaveServer ) {
return "Slave server: " + slaveServer.getName();
}
public boolean addSpoonBrowser( String name, String urlString ) {
return addSpoonBrowser( name, urlString, true, null, true );
}
public boolean addSpoonBrowser( String name, String urlString, boolean showControls ) {
return addSpoonBrowser( name, urlString, true, null, showControls );
}
public boolean addSpoonBrowser( String name, String urlString, LocationListener listener ) {
boolean ok = addSpoonBrowser( name, urlString, true, listener, true );
return ok;
}
public boolean addSpoonBrowser( String name, String urlString, LocationListener listener, boolean showControls ) {
return addSpoonBrowser( name, urlString, true, listener, showControls );
}
public boolean addSpoonBrowser( String name, String urlString, boolean isURL, LocationListener listener ) {
return addSpoonBrowser( name, urlString, isURL, listener, true );
}
public boolean addSpoonBrowser( String name, String urlString, boolean isURL, LocationListener listener, boolean showControls ) {
TabSet tabfolder = spoon.tabfolder;
try {
// OK, now we have the HTML, create a new browset tab.
// See if there already is a tab for this browser
// If no, add it
// If yes, select that tab
//
TabMapEntry tabMapEntry = findTabMapEntry( name, ObjectType.BROWSER );
if ( tabMapEntry == null ) {
CTabFolder cTabFolder = tabfolder.getSwtTabset();
final SpoonBrowser browser = new SpoonBrowser( cTabFolder, spoon, urlString, isURL, showControls, listener );
browser.getBrowser().addOpenWindowListener( new OpenWindowListener() {
@Override
public void open( WindowEvent event ) {
if ( event.required ) {
event.browser = browser.getBrowser();
}
}
} );
PropsUI props = PropsUI.getInstance();
TabItem tabItem = new TabItem( tabfolder, name, name, props.getSashWeights() );
tabItem.setImage( GUIResource.getInstance().getImageLogoSmall() );
tabItem.setControl( browser.getComposite() );
tabMapEntry =
new TabMapEntry( tabItem, isURL ? urlString : null, name, null, null, browser, ObjectType.BROWSER );
tabMap.add( tabMapEntry );
}
int idx = tabfolder.indexOf( tabMapEntry.getTabItem() );
// keep the focus on the graph
tabfolder.setSelected( idx );
return true;
} catch ( Throwable e ) {
boolean ok = false;
if ( isURL ) {
// Retry to show the welcome page in an external browser.
//
Status status = Launch.openURL( urlString );
ok = status.equals( Status.Success );
}
if ( !ok ) {
// Log an error
//
log.logError( "Unable to open browser tab", e );
return false;
} else {
return true;
}
}
}
public TabMapEntry findTabMapEntry( String tabItemText, ObjectType objectType ) {
for ( TabMapEntry entry : tabMap ) {
if ( !entry.getTabItem().isDisposed() ) {
if ( objectType == entry.getObjectType() && entry.getTabItem().getText().equalsIgnoreCase( tabItemText ) ) {
return entry;
}
}
}
return null;
}
public TabMapEntry findTabMapEntry( Object managedObject ) {
for ( TabMapEntry entry : tabMap ) {
if ( !entry.getTabItem().isDisposed() ) {
Object entryManagedObj = entry.getObject().getManagedObject();
// make sure they are the same class before comparing them
if ( entryManagedObj != null && managedObject != null ) {
if ( entryManagedObj.getClass().equals( managedObject.getClass() ) ) {
if ( entryManagedObj.equals( managedObject ) ) {
return entry;
}
}
}
}
}
return null;
}
/**
* Finds the tab for the transformation that matches the metadata provided (either the file must be the same or the
* repository id).
*
* @param trans
* Transformation metadata to look for
* @return Tab with transformation open whose metadata matches {@code trans} or {@code null} if no tab exists.
* @throws KettleFileException
* If there is a problem loading the file object for an open transformation with an invalid a filename.
*/
public TabMapEntry findTabForTransformation( TransMeta trans ) throws KettleFileException {
// File for the transformation we're looking for. It will be loaded upon first request.
FileObject transFile = null;
for ( TabMapEntry entry : tabMap ) {
if ( entry != null && !entry.getTabItem().isDisposed() ) {
if ( trans.getFilename() != null && entry.getFilename() != null ) {
// If the entry has a file name it is the same as trans iff. they originated from the same files
FileObject entryFile = KettleVFS.getFileObject( entry.getFilename() );
if ( transFile == null ) {
transFile = KettleVFS.getFileObject( trans.getFilename() );
}
if ( entryFile.equals( transFile ) ) {
return entry;
}
} else if ( trans.getObjectId() != null && entry.getObject() != null ) {
EngineMetaInterface meta = entry.getObject().getMeta();
if ( meta != null && trans.getObjectId().equals( meta.getObjectId() ) ) {
// If the transformation has an object id and the entry shares the same id they are the same
return entry;
}
}
}
}
// No tabs for the transformation exist and are not disposed
return null;
}
/**
* Rename the tabs
*/
public void renameTabs() {
List<TabMapEntry> list = new ArrayList<TabMapEntry>( tabMap );
for ( TabMapEntry entry : list ) {
if ( entry.getTabItem().isDisposed() ) {
// this should not be in the map, get rid of it.
tabMap.remove( entry.getObjectName() );
continue;
}
// TabItem before = entry.getTabItem();
// PDI-1683: need to get the String here, otherwise using only the "before" instance below, the reference gets
// changed and result is always the same
// String beforeText=before.getText();
//
Object managedObject = entry.getObject().getManagedObject();
if ( managedObject != null ) {
if ( entry.getObject() instanceof AbstractGraph ) {
AbstractMeta meta = (AbstractMeta) managedObject;
String tabText = makeTabName( meta, entry.isShowingLocation() );
entry.getTabItem().setText( tabText );
String toolTipText = BaseMessages.getString( PKG, "Spoon.TabTrans.Tooltip", tabText );
if ( entry.getObject() instanceof JobGraph ) {
toolTipText = BaseMessages.getString( PKG, "Spoon.TabJob.Tooltip", tabText );
}
if ( Const.isWindows() && !Utils.isEmpty( meta.getFilename() ) ) {
toolTipText += Const.CR + Const.CR + meta.getFilename();
}
entry.getTabItem().setToolTipText( toolTipText );
}
}
}
spoon.setShellText();
}
public void addTab( TabMapEntry entry ) {
tabMap.add( entry );
}
public String makeTabName( EngineMetaInterface transMeta, boolean showLocation ) {
if ( Utils.isEmpty( transMeta.getName() ) && Utils.isEmpty( transMeta.getFilename() ) ) {
return Spoon.STRING_TRANS_NO_NAME;
}
if ( Utils.isEmpty( transMeta.getName() )
|| spoon.delegates.trans.isDefaultTransformationName( transMeta.getName() ) ) {
transMeta.nameFromFilename();
}
String name = "";
if ( showLocation ) {
if ( !Utils.isEmpty( transMeta.getFilename() ) ) {
// Regular file...
//
name += transMeta.getFilename() + " : ";
} else {
// Repository object...
//
name += transMeta.getRepositoryDirectory().getPath() + " : ";
}
}
name += transMeta.getName();
if ( showLocation ) {
ObjectRevision version = transMeta.getObjectRevision();
if ( version != null ) {
name += " : r" + version.getName();
}
}
return name;
}
public void tabSelected( TabItem item ) {
// See which core objects to show
//
for ( TabMapEntry entry : tabMap ) {
boolean isAbstractGraph = ( entry.getObject() instanceof AbstractGraph );
if ( item.equals( entry.getTabItem() ) ) {
if ( isAbstractGraph ) {
EngineMetaInterface meta = entry.getObject().getMeta();
if ( meta != null ) {
meta.setInternalKettleVariables();
}
if ( spoon.getCoreObjectsState() != SpoonInterface.STATE_CORE_OBJECTS_SPOON ) {
spoon.refreshCoreObjects();
}
( (AbstractGraph) entry.getObject() ).setFocus();
}
break;
}
}
// Also refresh the tree
spoon.refreshTree();
spoon.setShellText(); // calls also enableMenus() and markTabsChanged()
}
}