/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.runtime.ui.views;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;
import net.jcip.annotations.GuardedBy;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.navigator.ICommonContentExtensionSite;
import org.eclipse.ui.navigator.ICommonContentProvider;
import org.eclipse.wst.server.core.IServer;
import org.jboss.tools.as.wst.server.ui.xpl.ServerToolTip;
import org.teiid.designer.runtime.DqpPlugin;
import org.teiid.designer.runtime.spi.ExecutionConfigurationEvent;
import org.teiid.designer.runtime.spi.ExecutionConfigurationEvent.EventType;
import org.teiid.designer.runtime.spi.IExecutionConfigurationListener;
import org.teiid.designer.runtime.spi.ITeiidServer;
import org.teiid.designer.runtime.ui.server.RuntimeAssistant;
import org.teiid.designer.runtime.ui.views.content.AbstractTeiidFolder;
import org.teiid.designer.runtime.ui.views.content.DataSourcesFolder;
import org.teiid.designer.runtime.ui.views.content.ITeiidContainerNode;
import org.teiid.designer.runtime.ui.views.content.ITeiidContentNode;
import org.teiid.designer.runtime.ui.views.content.ITeiidResourceNode;
import org.teiid.designer.runtime.ui.views.content.TeiidDataNode;
import org.teiid.designer.runtime.ui.views.content.TeiidResourceNode;
import org.teiid.designer.runtime.ui.views.content.TranslatorsFolder;
import org.teiid.designer.runtime.ui.views.content.VdbsFolder;
import org.teiid.designer.ui.common.util.UiUtil;
import org.teiid.designer.ui.viewsupport.ModelerUiViewUtils;
/**
* Class provides content and label information for ConnectorBindings and ModelInfos in ConnectorsView
*
* @since 8.0
*/
public class TeiidServerContentProvider implements ICommonContentProvider {
/** Represents a pending request in the tree. */
private static final Object PENDING = new Object();
private static final Object REFRESH = new Object();
private ConcurrentMap<ITeiidContainerNode, Object> pendingUpdates = new ConcurrentHashMap<ITeiidContainerNode, Object>();
private transient TreeViewer viewer;
/**
* Servers that a connection can't be established. Value is the last time establishing a connection was tried.
*/
@GuardedBy( "offlineServersLock" )
private final Map<ITeiidServer, Long> offlineServerMap = new HashMap<ITeiidServer, Long>();
private boolean showVDBs = true;
private boolean showDataSources = true;
private boolean showTranslators = true;
private class RefreshThread extends Thread {
private boolean die = false;
private boolean busy = false;
private LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<Object>();
public RefreshThread() {
super(TeiidServerContentProvider.this + "." + RefreshThread.class.getSimpleName()); //$NON-NLS-1$
setDaemon(true);
}
/**
* End the thread
*/
public void die() {
die = true;
}
public void scheduleRefresh() {
if (die)
return;
queue.add(REFRESH);
}
public void schedulePending() {
if (die)
return;
queue.add(PENDING);
}
@Override
public void run() {
while (! Thread.currentThread().isInterrupted() && ! die) {
try {
if (! busy) {
Object queueObject = queue.take();
if (PENDING.equals(queueObject))
loadPending();
else if (REFRESH.equals(queueObject))
loadRefresh();
}
Thread.yield();
} catch (InterruptedException inte) {
Thread.currentThread().interrupt();
}
}
}
private void loadPending() {
busy = true;
final List<ITeiidContainerNode> updated = new ArrayList<ITeiidContainerNode>(pendingUpdates.size());
for (ITeiidContainerNode node : pendingUpdates.keySet()) {
try {
node.load();
updated.add(node);
} catch (Exception e) {
}
}
if (viewer == null) {
pendingUpdates.keySet().clear();
} else {
UiUtil.runInSwtThread(new Runnable() {
@Override
public void run() {
try {
if (viewer.getControl().isDisposed())
return;
for (Object node : updated) {
pendingUpdates.remove(node);
viewer.refresh(node);
if (node instanceof ITeiidResourceNode) {
viewer.setExpandedState(node, true);
viewer.expandToLevel(node, 2);
}
}
} finally {
busy = false;
}
}
}, true);
}
}
private void loadRefresh() {
busy = true;
UiUtil.runInSwtThread(new Runnable() {
@Override
public void run() {
try {
if (viewer.getTree().isDisposed())
return;
ITeiidResourceNode trn = null;
for (TreePath o : viewer.getExpandedTreePaths()) {
Object element = o.getLastSegment();
if (isTeiidResourceNode(element)) {
trn = (ITeiidResourceNode) element;
break;
}
Object[] children = getChildren(element);
for (Object child : children) {
if (isTeiidResourceNode(child)) {
trn = (ITeiidResourceNode) child;
break;
}
}
if (isTeiidResourceNode(trn)) {
break;
}
}
// Refresh the viewer
viewer.refresh();
if (trn != null) {
viewer.setExpandedState(trn, true);
viewer.expandToLevel(trn, 2);
}
// Refresh the Model Explorer too
ModelerUiViewUtils.refreshModelExplorerResourceNavigatorTree();
} finally {
busy = false;
}
}
}, false);
}
}
private IExecutionConfigurationListener configListener = new IExecutionConfigurationListener() {
@Override
public void configurationChanged( final ExecutionConfigurationEvent event ) {
if (EventType.CONNECTING.equals(event.getEventType())) {
refreshThread.schedulePending();
return;
}
if(EventType.CONNECTED.equals(event.getEventType())) {
/*
* This event is followed straight-after by a refresh event.
* No point in refresh twice for performance reasons.
*/
return;
}
refreshThread.scheduleRefresh();
}
};
private RefreshThread refreshThread = new RefreshThread();
private ServerToolTip tooltip;
/**
* Content will include VDBs, translators, and data sources.
* @since 5.0
*/
public TeiidServerContentProvider() {
super();
refreshThread.start();
// Wire as listener to server manager and to receive configuration changes
DqpPlugin.getInstance().getServerManager().addListener(configListener);
}
/**
* Used for display of server content in dialogs
*
* @param showVDBs
* @param showTranslators
* @param showDataSources
*
* @since 5.0
*/
public TeiidServerContentProvider( boolean showVDBs,
boolean showTranslators,
boolean showDataSources ) {
this();
this.showVDBs = showVDBs;
this.showTranslators = showTranslators;
this.showDataSources = showDataSources;
}
/**
* @return whether to show data sources
*/
public boolean isShowDataSources() {
return this.showDataSources;
}
/**
* @return whether to show vdbs
*/
public boolean isShowVDBs() {
return this.showVDBs;
}
/**
* @return the showTranslators
*/
public boolean isShowTranslators() {
return this.showTranslators;
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
* @since 4.2
*/
@Override
public Object[] getChildren( Object parentElement ) {
if (parentElement == null)
return new Object[0];
if (parentElement instanceof IServer) {
ITeiidResourceNode node = TeiidResourceNode.getInstance((IServer) parentElement, this);
node.setDirty();
return new Object[] { node };
} else if (parentElement instanceof ITeiidContainerNode) {
ITeiidContainerNode<?> container = (ITeiidContainerNode<?>) parentElement;
if (pendingUpdates.containsKey(container)) {
return new Object[] { PENDING };
}
List<? extends ITeiidContentNode<?>> children = container.getChildren();
if (children == null) {
pendingUpdates.putIfAbsent(container, PENDING);
refreshThread.schedulePending();
return new Object[] { PENDING };
}
return children.toArray();
}
return new Object[0];
}
/**
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
* @since 4.2
*/
@Override
public Object[] getElements( Object inputElement ) {
return getChildren(inputElement);
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
* @since 4.2
*/
@Override
public Object getParent( Object element ) {
if (element instanceof ITeiidContentNode) {
Object parent = ((ITeiidContentNode<?>) element).getContainer();
if (parent == null) {
parent = ((ITeiidContentNode<?>) element).getServer();
}
return parent;
}
return null;
}
/**
* @param server the server whose Data Source folder is being requested
* @return the folder
*/
public Object getDataSourceFolder(Object server) {
for (Object child : getChildren(server)) {
if (child instanceof DataSourcesFolder) {
return child;
}
}
return null;
}
/**
* @param server the server whose Translators folder is being requested
* @return the folder
*/
public Object getTranslatorFolder(Object server) {
for (Object child : getChildren(server)) {
if (child instanceof TranslatorsFolder) {
return child;
}
}
return null;
}
/**
* @param server the server whose VDBs folder is being requested
* @return the folder
*/
public Object getVdbFolder(Object server) {
for (Object child : getChildren(server)) {
if (child instanceof VdbsFolder) {
return child;
}
}
return null;
}
static Object getPending() {
return PENDING;
}
/**
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
* @since 4.2
*/
@Override
public boolean hasChildren( Object element ) {
if (element instanceof IServer) {
return true;
} else if (element instanceof ITeiidContainerNode) {
return true;
}
return false;
}
private boolean isTeiidResourceNode(Object o) {
return o instanceof ITeiidResourceNode;
}
/**
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object,
* java.lang.Object)
* @since 4.2
*/
@Override
public void init(ICommonContentExtensionSite aConfig) {
/*
* Restore state does not get called conventionally since the provider
* is an adjunct to the viewer so call in manually when it is initialised.
*/
restoreState(aConfig.getMemento());
}
@Override
public void inputChanged( Viewer viewer, Object oldInput, Object newInput ) {
if (viewer instanceof TreeViewer) {
this.viewer = (TreeViewer) viewer;
if( tooltip != null )
tooltip.deactivate();
tooltip = new ServerToolTip(this.viewer.getTree()) {
@Override
protected boolean isMyType(Object selected) {
return RuntimeAssistant.adapt(selected, AbstractTeiidFolder.class) != null ||
RuntimeAssistant.adapt(selected, TeiidDataNode.class) != null ||
RuntimeAssistant.adapt(selected, ITeiidServer.class) != null;
}
@Override
protected void fillStyledText(Composite parent, StyledText sText, Object o) {
if (o instanceof TreeItem) {
String text = ((TreeItem) o).getData().toString();
sText.setText(text.replace("\n", "<br>")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
};
tooltip.setShift(new Point(15, 8));
tooltip.setPopupDelay(500); // in ms
tooltip.setHideOnMouseDown(true);
tooltip.activate();
} else {
this.viewer = null;
}
}
@Override
public void dispose() {
viewer = null;
pendingUpdates.clear();
DqpPlugin.getInstance().getServerManager().removeListener(configListener);
refreshThread.die();
refreshThread = null;
}
@Override
public void saveState(IMemento memento) {
// Nothing to do
}
@Override
public void restoreState(IMemento aMemento) {
// Nothing to do
}
}