/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* 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.jkiss.dbeaver.ui.data.editors;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.*;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.core.DBeaverUI;
import org.jkiss.dbeaver.model.DBPEvaluationContext;
import org.jkiss.dbeaver.model.DBPMessageType;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.data.DBDAttributeBinding;
import org.jkiss.dbeaver.model.data.DBDContent;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.runtime.DBRRunnableWithProgress;
import org.jkiss.dbeaver.model.runtime.DefaultProgressMonitor;
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
import org.jkiss.dbeaver.model.struct.DBSObject;
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
import org.jkiss.dbeaver.ui.DBeaverIcons;
import org.jkiss.dbeaver.ui.UIIcon;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.ui.data.IStreamValueEditor;
import org.jkiss.dbeaver.ui.data.IStreamValueManager;
import org.jkiss.dbeaver.ui.data.IValueController;
import org.jkiss.dbeaver.ui.data.registry.StreamValueManagerDescriptor;
import org.jkiss.dbeaver.ui.data.registry.ValueManagerRegistry;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.List;
/**
* ControlPanelEditor
*/
public class ContentPanelEditor extends BaseValueEditor<Control> implements IAdaptable {
private static final Log log = Log.getLog(ContentPanelEditor.class);
private static Map<String, String> valueToManagerMap = new HashMap<>();
private Map<StreamValueManagerDescriptor, IStreamValueManager.MatchType> streamManagers;
private StreamValueManagerDescriptor curStreamManager;
private IStreamValueEditor<Control> streamEditor;
private Control editorControl;
public ContentPanelEditor(IValueController controller) {
super(controller);
}
@Override
public void contributeActions(@NotNull IContributionManager manager, @NotNull IValueController controller) throws DBCException {
if (streamManagers != null) {
manager.add(new ContentTypeSwitchAction());
}
if (streamEditor != null) {
streamEditor.contributeActions(manager, control);
}
}
@Override
public void primeEditorValue(@Nullable final Object value) throws DBException
{
final DBDContent content = (DBDContent) valueController.getValue();
if (content == null) {
valueController.showMessage("NULL content value. Must be DBDContent.", DBPMessageType.ERROR);
return;
}
if (streamEditor == null) {
valueController.showMessage("NULL content editor.", DBPMessageType.ERROR);
return;
}
DBeaverUI.runInUI(valueController.getValueSite().getWorkbenchWindow(), new DBRRunnableWithProgress() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
streamEditor.primeEditorValue(monitor, control, content);
} catch (Throwable e) {
log.debug(e);
valueController.showMessage(e.getMessage(), DBPMessageType.ERROR);
}
}
});
}
@Override
public Object extractEditorValue() throws DBException
{
final DBDContent content = (DBDContent) valueController.getValue();
if (content == null) {
log.warn("NULL content value. Must be DBDContent.");
} else if (streamEditor == null) {
log.warn("NULL content editor.");
} else {
try {
streamEditor.extractEditorValue(new VoidProgressMonitor(), control, content);
} catch (Throwable e) {
log.debug(e);
valueController.showMessage(e.getMessage(), DBPMessageType.ERROR);
}
}
return content;
}
@Override
protected Control createControl(Composite editPlaceholder)
{
final DBDContent content = (DBDContent) valueController.getValue();
if (curStreamManager == null) {
detectStreamManager(content);
}
if (curStreamManager != null) {
try {
streamEditor = curStreamManager.getInstance().createPanelEditor(valueController);
} catch (Throwable e) {
UIUtils.showErrorDialog(editPlaceholder.getShell(), "No stream editor", "Can't create stream editor", e);
}
}
if (streamEditor == null) {
return UIUtils.createInfoLabel(editPlaceholder, "No Editor");
}
editorControl = streamEditor.createControl(valueController);
return editorControl;
}
private void detectStreamManager(final DBDContent content) {
StreamManagerDetectJob detectJob = new StreamManagerDetectJob(content);
RuntimeUtils.runTask(detectJob, "Detect stream editor", 5000);
}
private void setStreamManager(StreamValueManagerDescriptor newManager) {
curStreamManager = newManager;
if (curStreamManager != null) {
// Save manager setting for current attribute
String valueId = makeValueId();
valueToManagerMap.put(valueId, curStreamManager.getId());
valueController.refreshEditor();
}
}
private String makeValueId() {
String valueId;
DBSTypedObject valueType = valueController.getValueType();
if (valueType instanceof DBDAttributeBinding) {
valueType = ((DBDAttributeBinding) valueType).getAttribute();
}
if (valueType instanceof DBSObject) {
DBSObject object = (DBSObject) valueType;
valueId = DBUtils.getObjectFullName(object, DBPEvaluationContext.DDL);
if (object.getParentObject() != null) {
valueId = DBUtils.getObjectFullName(object.getParentObject(), DBPEvaluationContext.DDL) + ":" + valueId;
}
} else {
valueId = valueController.getValueName();
}
String dsId = "unknown";
if (valueController.getExecutionContext() != null) {
dsId = valueController.getExecutionContext().getDataSource().getContainer().getId();
}
return dsId + ":" + valueId;
}
@Override
public <T> T getAdapter(Class<T> adapter) {
if (streamEditor != null) {
if (adapter.isAssignableFrom(streamEditor.getClass())) {
return adapter.cast(streamEditor);
}
if (streamEditor instanceof IAdaptable) {
return ((IAdaptable) streamEditor).getAdapter(adapter);
}
}
return null;
}
private class ContentTypeSwitchAction extends Action implements SelectionListener {
private Menu menu;
ContentTypeSwitchAction() {
super(null, Action.AS_DROP_DOWN_MENU);
setImageDescriptor(DBeaverIcons.getImageDescriptor(UIIcon.PAGES));
setToolTipText("Content viewer settings");
}
@Override
public void runWithEvent(Event event)
{
if (event.widget instanceof ToolItem) {
ToolItem toolItem = (ToolItem) event.widget;
Menu menu = createMenu(toolItem);
Rectangle bounds = toolItem.getBounds();
Point point = toolItem.getParent().toDisplay(bounds.x, bounds.y + bounds.height);
menu.setLocation(point.x, point.y);
menu.setVisible(true);
}
}
private Menu createMenu(ToolItem toolItem) {
if (menu == null) {
ToolBar toolBar = toolItem.getParent();
menu = new Menu(toolBar);
List<StreamValueManagerDescriptor> managers = new ArrayList<>(streamManagers.keySet());
Collections.sort(managers, new Comparator<StreamValueManagerDescriptor>() {
@Override
public int compare(StreamValueManagerDescriptor o1, StreamValueManagerDescriptor o2) {
return o1.getLabel().compareTo(o2.getLabel());
}
});
for (StreamValueManagerDescriptor manager : managers) {
MenuItem item = new MenuItem(menu, SWT.RADIO);
item.setText(manager.getLabel());
item.setData(manager);
item.addSelectionListener(this);
}
MenuManager menuManager = new MenuManager();
try {
streamEditor.contributeSettings(menuManager, editorControl);
for (IContributionItem item : menuManager.getItems()) {
item.fill(menu, -1);
}
} catch (DBCException e) {
log.error(e);
}
toolBar.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
menu.dispose();
}
});
}
for (MenuItem item : menu.getItems()) {
if (item.getData() instanceof StreamValueManagerDescriptor) {
item.setSelection(item.getData() == curStreamManager);
}
}
return menu;
}
@Override
public void widgetSelected(SelectionEvent e) {
for (MenuItem item : menu.getItems()) {
if (item.getSelection()) {
Object itemData = item.getData();
if (itemData instanceof StreamValueManagerDescriptor) {
StreamValueManagerDescriptor newManager = (StreamValueManagerDescriptor) itemData;
if (newManager != curStreamManager) {
setStreamManager(newManager);
}
}
}
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
}
private class StreamManagerDetectJob implements DBRRunnableWithProgress {
private final DBDContent content;
StreamManagerDetectJob(DBDContent content) {
this.content = content;
}
@Override
public void run(DBRProgressMonitor monitor) {
monitor.beginTask("Detect appropriate editor", 1);
try {
streamManagers = ValueManagerRegistry.getInstance().getApplicableStreamManagers(monitor, valueController.getValueType(), content);
String savedManagerId = valueToManagerMap.get(makeValueId());
if (savedManagerId != null) {
curStreamManager = findManager(savedManagerId);
}
if (curStreamManager == null) {
curStreamManager = findManager(IStreamValueManager.MatchType.EXCLUSIVE);
if (curStreamManager == null)
curStreamManager = findManager(IStreamValueManager.MatchType.PRIMARY);
if (curStreamManager == null)
curStreamManager = findManager(IStreamValueManager.MatchType.DEFAULT);
if (curStreamManager == null)
curStreamManager = findManager(IStreamValueManager.MatchType.APPLIES);
if (curStreamManager == null) {
throw new DBException("Can't find appropriate stream manager");
}
}
} catch (Exception e) {
valueController.showMessage(e.getMessage(), DBPMessageType.ERROR);
} finally {
monitor.done();
}
}
private StreamValueManagerDescriptor findManager(IStreamValueManager.MatchType matchType) {
for (Map.Entry<StreamValueManagerDescriptor, IStreamValueManager.MatchType> entry : streamManagers.entrySet()) {
if (entry.getValue() == matchType) {
return entry.getKey();
}
}
return null;
}
private StreamValueManagerDescriptor findManager(String id) {
for (Map.Entry<StreamValueManagerDescriptor, IStreamValueManager.MatchType> entry : streamManagers.entrySet()) {
if (entry.getKey().getId().equals(id)) {
return entry.getKey();
}
}
return null;
}
}
}