/*
* 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.editors.content;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.IContributionManager;
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.Control;
import org.eclipse.ui.*;
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.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.ui.UIUtils;
import org.jkiss.dbeaver.ui.data.IStreamValueManager;
import org.jkiss.dbeaver.ui.data.IValueController;
import org.jkiss.dbeaver.ui.data.IValueEditorStandalone;
import org.jkiss.dbeaver.ui.data.registry.StreamValueManagerDescriptor;
import org.jkiss.dbeaver.ui.data.registry.ValueManagerRegistry;
import org.jkiss.dbeaver.ui.dialogs.ColumnInfoPanel;
import org.jkiss.dbeaver.ui.editors.MultiPageAbstractEditor;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* LOBEditor
*/
public class ContentEditor extends MultiPageAbstractEditor implements IValueEditorStandalone, IResourceChangeListener
{
@Override
public ContentEditorInput getEditorInput()
{
return (ContentEditorInput)super.getEditorInput();
}
@Nullable
public static ContentEditor openEditor(IValueController valueController, DBDContent content)
{
ContentEditorInput editorInput;
// Save data to file
try {
LOBInitializer initializer = new LOBInitializer(
valueController,
content);
//valueController.getValueSite().getWorkbenchWindow().run(true, true, initializer);
DBeaverUI.runInProgressService(initializer);
editorInput = initializer.editorInput;
} catch (Throwable e) {
if (e instanceof InvocationTargetException) {
e = ((InvocationTargetException)e).getTargetException();
}
UIUtils.showErrorDialog(valueController.getValueSite().getShell(), "Cannot open content editor", null, e);
return null;
}
try {
return (ContentEditor) valueController.getValueSite().getWorkbenchWindow().getActivePage().openEditor(
editorInput,
ContentEditor.class.getName());
}
catch (PartInitException e) {
log.error("Can't open CONTENT editorPart", e);
return null;
}
}
//public static final long MAX_TEXT_LENGTH = 10 * 1024 * 1024;
//public static final long MAX_IMAGE_LENGTH = 10 * 1024 * 1024;
private static final Log log = Log.getLog(ContentEditor.class);
static class ContentPartInfo {
IEditorPart editorPart;
boolean isDefault;
boolean activated;
public int index = -1;
private ContentPartInfo(IEditorPart editorPart, boolean isDefault) {
this.editorPart = editorPart;
this.isDefault = isDefault;
}
}
private static class LOBInitializer implements DBRRunnableWithProgress {
IValueController valueController;
DBDContent content;
IEditorPart[] editorParts;
IEditorPart defaultPart;
ContentEditorInput editorInput;
public LOBInitializer(IValueController valueController, DBDContent content) {
this.valueController = valueController;
this.content = content;
}
private LOBInitializer(IValueController valueController, IEditorPart[] editorParts, IEditorPart defaultPart, @Nullable ContentEditorInput editorInput)
{
this.valueController = valueController;
this.editorParts = editorParts;
this.defaultPart = defaultPart;
this.editorInput = editorInput;
}
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException
{
try {
if (content != null && editorParts == null) {
Map<StreamValueManagerDescriptor, IStreamValueManager.MatchType> streamManagers =
ValueManagerRegistry.getInstance().getApplicableStreamManagers(monitor, valueController.getValueType(), content);
List<IEditorPart> parts = new ArrayList<>();
IStreamValueManager.MatchType defaultMatch = null;
for (Map.Entry<StreamValueManagerDescriptor, IStreamValueManager.MatchType> entry : streamManagers.entrySet()) {
IStreamValueManager streamValueManager = entry.getKey().getInstance();
try {
IEditorPart editorPart = streamValueManager.createEditorPart(valueController);
IStreamValueManager.MatchType matchType = entry.getValue();
if (defaultPart == null) {
defaultPart = editorPart;
defaultMatch = matchType;
} else {
boolean setDefault = false;
switch (matchType) {
case EXCLUSIVE:
case PRIMARY:
setDefault = true;
break;
case DEFAULT:
setDefault = (defaultMatch == IStreamValueManager.MatchType.APPLIES);
break;
default:
break;
}
if (setDefault) {
defaultPart = editorPart;
defaultMatch = matchType;
}
}
parts.add(editorPart);
} catch (DBException e) {
log.error(e);
}
}
editorParts = parts.toArray(new IEditorPart[parts.size()]);
}
if (editorInput == null) {
editorInput = new ContentEditorInput(
valueController,
editorParts,
defaultPart,
monitor);
} else {
editorInput.refreshContent(monitor, valueController);
}
} catch (DBException e) {
throw new InvocationTargetException(e);
}
}
}
private List<ContentPartInfo> contentParts = new ArrayList<>();
private ColumnInfoPanel infoPanel;
private boolean dirty;
private boolean partsLoaded;
private boolean saveInProgress;
public ContentEditor()
{
}
@Override
public void doSave(final IProgressMonitor monitor)
{
if (!isDirty()) {
// Nothing to save
return;
}
// Execute save in UI thread
DBeaverUI.syncExec(new Runnable() {
@Override
public void run() {
try {
// Check for dirty parts
final List<IEditorPart> dirtyParts = new ArrayList<>();
for (ContentPartInfo partInfo : contentParts) {
if (partInfo.activated && partInfo.editorPart.isDirty()) {
dirtyParts.add(partInfo.editorPart);
}
}
IEditorPart dirtyPart = null;
if (dirtyParts.isEmpty()) {
// No modified parts - no additional save required
} else if (dirtyParts.size() == 1) {
// Single part modified - save it
dirtyPart = dirtyParts.get(0);
} else {
// Multiple parts modified - need to choose one
dirtyPart = SelectContentPartDialog.selectContentPart(getSite().getShell(), dirtyParts);
}
if (dirtyPart != null) {
saveInProgress = true;
try {
dirtyPart.doSave(monitor);
} finally {
saveInProgress = false;
}
}
// Set dirty flag - if error will occur during content save
// then document remains dirty
ContentEditor.this.dirty = true;
ContentEditorInput editorInput = getEditorInput();
editorInput.updateContentFromFile(monitor);
editorInput.getValueController().updateValue(editorInput.getContent(), true);
// Close editor
closeValueEditor();
} catch (Exception e) {
UIUtils.showErrorDialog(
getSite().getShell(),
"Can't save content",
"Can't save content to database",
e);
}
}
});
}
@Override
public void doSaveAs()
{
}
@Override
public void init(IEditorSite site, IEditorInput input)
throws PartInitException
{
super.init(site, input);
setPartName(input.getName());
DBDContent content = getContent();
if (content == null) {
return;
}
// Fill nested editorParts info
IEditorPart[] editorParts = getEditorInput().getEditors();
for (IEditorPart editorPart : editorParts) {
contentParts.add(new ContentPartInfo(editorPart, editorPart == getEditorInput().getDefaultEditor()));
//editorPart.init(site, input);
}
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}
@Override
public void dispose()
{
this.partsLoaded = true;
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
if (getEditorInput() != null) {
// Release CONTENT input resources
try {
getEditorInput().release();
} catch (Throwable e) {
log.warn("Error releasing CONTENT input", e);
}
}
super.dispose();
}
@Override
public boolean isDirty()
{
if (dirty) {
return true;
}
for (ContentPartInfo contentPart : contentParts) {
if (contentPart.activated && contentPart.editorPart.isDirty()) {
return true;
}
}
return false;
}
@Override
public void setDirty(boolean dirty) {
this.dirty = false;
}
@Override
public boolean isSaveAsAllowed()
{
return false;
}
@Override
protected IEditorSite createSite(IEditorPart editor)
{
return new ContentEditorSite(this, editor);
}
@Override
protected void createPages() {
super.createPages();
DBDContent content = getContent();
if (content == null) {
return;
}
ContentPartInfo defaultPage = null;
for (ContentPartInfo contentPart : contentParts) {
if (contentPart.isDefault) {
defaultPage = contentPart;
}
IEditorPart editorPart = contentPart.editorPart;
try {
int index = addPage(editorPart, getEditorInput());
setPageText(index, editorPart.getTitle());
setPageImage(index, editorPart.getTitleImage());
contentPart.activated = true;
contentPart.index = index;
} catch (PartInitException e) {
log.error(e);
}
}
if (defaultPage != null) {
setActiveEditor(defaultPage.editorPart);
}
this.partsLoaded = true;
}
@Override
public void removePage(int pageIndex) {
for (ContentPartInfo contentPart : contentParts) {
if (contentPart.index == pageIndex) {
contentPart.index = -1;
} else if (contentPart.index > pageIndex) {
contentPart.index--;
}
}
super.removePage(pageIndex);
}
@Override
protected Composite createPageContainer(Composite parent)
{
Composite panel = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(1, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
layout.horizontalSpacing = 0;
panel.setLayout(layout);
if (parent.getLayout() instanceof GridLayout) {
panel.setLayoutData(new GridData(GridData.FILL_BOTH));
}
{
IValueController valueController = getValueController();
assert valueController != null;
infoPanel = new ColumnInfoPanel(panel, SWT.NONE, valueController);
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.exclude = true;
infoPanel.setLayoutData(gd);
infoPanel.setVisible(false);
}
Composite editotPanel = new Composite(panel, SWT.NONE);
layout = new GridLayout(1, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
layout.horizontalSpacing = 0;
editotPanel.setLayout(layout);
editotPanel.setLayoutData(new GridData(GridData.FILL_BOTH));
return editotPanel;
}
void toggleInfoBar()
{
boolean visible = infoPanel.isVisible();
visible = !visible;
GridData gd = new GridData(GridData.FILL_HORIZONTAL);
gd.exclude = !visible;
infoPanel.setLayoutData(gd);
infoPanel.setVisible(visible);
infoPanel.getParent().layout();
}
@Nullable
DBDContent getContent()
{
IValueController valueController = getValueController();
Object value = valueController == null? null : valueController.getValue();
if (value instanceof DBDContent) {
return (DBDContent) value;
} else {
return null;
}
}
@Nullable
public IValueController getValueController()
{
ContentEditorInput input = getEditorInput();
return input == null ? null : input.getValueController();
}
@Override
public void createControl() {
}
@Override
public Control getControl()
{
int activePage = getActivePage();
return activePage < 0 ? null : getControl(activePage);
}
@Override
public Object extractEditorValue() throws DBException
{
DBeaverUI.runInUI(new DBRRunnableWithProgress() {
@Override
public void run(DBRProgressMonitor monitor) throws InvocationTargetException, InterruptedException
{
try {
getEditorInput().updateContentFromFile(RuntimeUtils.getNestedMonitor(monitor));
} catch (DBException e) {
throw new InvocationTargetException(e);
}
}
});
return getEditorInput().getContent();
}
@Override
public void primeEditorValue(@Nullable Object value) throws DBException
{
ContentEditorInput input = getEditorInput();
IValueController valueController = input.getValueController();
LOBInitializer initializer = new LOBInitializer(valueController, input.getEditors(), input.getDefaultEditor(), input);
try {
//valueController.getValueSite().getWorkbenchWindow().run(true, true, initializer);
DBeaverUI.runInProgressService(initializer);
} catch (InvocationTargetException e) {
UIUtils.showErrorDialog(valueController.getValueSite().getShell(), "Cannot refresh content editor", null, e);
} catch (InterruptedException e) {
// ignore
}
}
@Override
public void contributeActions(@NotNull IContributionManager manager, @NotNull IValueController controller) throws DBCException {
}
@Override
public void showValueEditor()
{
this.getEditorSite().getWorkbenchWindow().getActivePage().activate(this);
}
@Override
public void closeValueEditor()
{
IWorkbenchPage workbenchPage = this.getEditorSite().getWorkbenchWindow().getActivePage();
if (workbenchPage != null) {
workbenchPage.closeEditor(this, false);
}
}
@Override
public void resourceChanged(IResourceChangeEvent event)
{
if (!partsLoaded || saveInProgress) {
// No content change before all parts are loaded
return;
}
IResourceDelta delta= event.getDelta();
if (delta == null) {
return;
}
delta = delta.findMember(ContentUtils.convertPathToWorkspacePath(getEditorInput().getPath()));
if (delta == null) {
return;
}
if (delta.getKind() == IResourceDelta.CHANGED &&
(delta.getFlags() & IResourceDelta.CONTENT) != 0)
{
// Content was changed somehow so mark editor as dirty
dirty = true;
DBeaverUI.asyncExec(new Runnable() {
@Override
public void run() {
firePropertyChange(PROP_DIRTY);
}
});
}
}
}