/* * 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.IStorage; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPathEditorInput; import org.eclipse.ui.IPersistableElement; import org.eclipse.ui.PartInitException; import org.eclipse.ui.editors.text.IEncodingSupport; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.core.DBeaverCore; import org.jkiss.dbeaver.model.DBIcon; import org.jkiss.dbeaver.model.DBPContextProvider; import org.jkiss.dbeaver.model.DBValueFormatting; import org.jkiss.dbeaver.model.data.DBDContent; import org.jkiss.dbeaver.model.data.DBDContentCached; import org.jkiss.dbeaver.model.data.DBDContentStorage; import org.jkiss.dbeaver.model.data.DBDContentStorageLocal; import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.dbeaver.model.exec.DBCExecutionContext; import org.jkiss.dbeaver.model.impl.BytesContentStorage; import org.jkiss.dbeaver.model.impl.StringContentStorage; import org.jkiss.dbeaver.model.impl.TemporaryContentStorage; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.runtime.LocalFileStorage; import org.jkiss.dbeaver.ui.DBeaverIcons; import org.jkiss.dbeaver.ui.data.IAttributeController; import org.jkiss.dbeaver.ui.data.IValueController; import org.jkiss.dbeaver.utils.ContentUtils; import org.jkiss.dbeaver.utils.GeneralUtils; import org.jkiss.dbeaver.utils.RuntimeUtils; import java.io.*; /** * ContentEditorInput */ public class ContentEditorInput implements IPathEditorInput, DBPContextProvider, IEncodingSupport { private static final Log log = Log.getLog(ContentEditorInput.class); private IValueController valueController; private IEditorPart[] editorParts; private IEditorPart defaultPart; private File contentFile; private boolean contentDetached = false; private String fileCharset; public ContentEditorInput( IValueController valueController, IEditorPart[] editorParts, IEditorPart defaultPart, DBRProgressMonitor monitor) throws DBException { this.valueController = valueController; this.editorParts = editorParts; this.defaultPart = defaultPart; this.fileCharset = getDefaultEncoding(); this.prepareContent(monitor); } public File getContentFile() { return contentFile; } public IValueController getValueController() { return valueController; } public void refreshContent(DBRProgressMonitor monitor, IValueController valueController) throws DBException { this.valueController = valueController; this.prepareContent(monitor); } IEditorPart[] getEditors() { return editorParts; } public IEditorPart getDefaultEditor() { return defaultPart; } @Override public boolean exists() { return false; } @Override public ImageDescriptor getImageDescriptor() { return DBeaverIcons.getImageDescriptor(DBIcon.TYPE_LOB); } @Override public String getName() { String inputName; if (valueController instanceof IAttributeController) { inputName = ((IAttributeController) valueController).getColumnId(); } else { inputName = valueController.getValueName(); } if (isReadOnly()) { inputName += " [Read Only]"; } return inputName; } @Nullable @Override public IPersistableElement getPersistable() { return null; } @Override public String getToolTipText() { return getName(); } @Nullable @Override public <T> T getAdapter(Class<T> adapter) { if (adapter == IStorage.class) { return adapter.cast(new LocalFileStorage(contentFile, fileCharset)); } return null; } public DBDContent getContent() throws DBCException { Object value = valueController.getValue(); if (value instanceof DBDContent) { return (DBDContent)value; } else { throw new DBCException("Value doesn't support streaming"); } } private void prepareContent(DBRProgressMonitor monitor) throws DBException { DBDContent content = getContent(); DBDContentStorage storage = content.getContents(monitor); if (contentDetached) { release(); contentDetached = false; } if (storage instanceof DBDContentStorageLocal) { // User content's storage directly contentFile = ((DBDContentStorageLocal)storage).getDataFile(); contentDetached = true; } else { // Copy content to local file try { // Create file if (contentFile == null) { String valueId; if (valueController instanceof IAttributeController) { valueId = ((IAttributeController) valueController).getColumnId(); } else { valueId = valueController.getValueName(); } contentFile = ContentUtils.createTempContentFile(monitor, DBeaverCore.getInstance(), valueId); } // Write value to file copyContentToFile(content, monitor); } catch (IOException e) { // Delete temp file if (contentFile != null && contentFile.exists()) { if (!contentFile.delete()) { log.warn("Can't delete temporary content file '" + contentFile.getAbsolutePath() + "'"); } } throw new DBException("Can't delete content file", e); } } // Mark file as readonly if (valueController.isReadOnly()) { markReadOnly(true); } } private void markReadOnly(boolean readOnly) throws DBException { if (!contentFile.setWritable(!readOnly)) { throw new DBException("Can't set content read-only"); } } public void release() { if (contentFile != null && !contentDetached) { if (!contentFile.delete()) { log.warn("Can't delete temp file '" + contentFile.getAbsolutePath() + "'"); } contentDetached = true; } } @Nullable @Override public IPath getPath() { return contentFile == null ? null : new Path(contentFile.getAbsolutePath()); } public boolean isReadOnly() { return valueController.isReadOnly(); } void saveToExternalFile(File file, IProgressMonitor monitor) throws CoreException { try (InputStream is = new FileInputStream(contentFile)) { ContentUtils.saveContentToFile( is, file, RuntimeUtils.makeMonitor(monitor)); } catch (IOException e) { throw new CoreException(GeneralUtils.makeExceptionStatus(e)); } } void loadFromExternalFile(File extFile, IProgressMonitor monitor) throws CoreException { try { try (InputStream inputStream = new FileInputStream(extFile)) { try (OutputStream outputStream = new FileOutputStream(contentFile)) { ContentUtils.copyStreams(inputStream, extFile.length(), outputStream, RuntimeUtils.makeMonitor(monitor)); } } } catch (Throwable e) { throw new CoreException(GeneralUtils.makeExceptionStatus(e)); } } private void copyContentToFile(DBDContent contents, DBRProgressMonitor monitor) throws DBException, IOException { DBDContentStorage storage = contents.getContents(monitor); markReadOnly(false); try (OutputStream os = new FileOutputStream(contentFile)) { if (contents.isNull()) { ContentUtils.copyStreams(new ByteArrayInputStream(new byte[0]), 0, os, monitor); } else { if (storage == null) { log.warn("Can't get data from null storage"); return; } try (InputStream is = storage.getContentStream()) { ContentUtils.copyStreams(is, storage.getContentLength(), os, monitor); } } } markReadOnly(valueController.isReadOnly()); } public void updateContentFromFile(IProgressMonitor monitor) throws DBException { if (valueController.isReadOnly()) { throw new DBCException("Can't update read-only value"); } DBRProgressMonitor localMonitor = RuntimeUtils.makeMonitor(monitor); DBDContent content = getContent(); DBDContentStorage storage = content.getContents(localMonitor); if (storage instanceof DBDContentStorageLocal) { // Nothing to update - we user content's storage contentDetached = true; } else if (storage instanceof DBDContentCached) { // Create new storage and pass it to content try (FileInputStream is = new FileInputStream(contentFile)) { if (storage instanceof StringContentStorage) { try (Reader reader = new InputStreamReader(is, fileCharset)) { storage = StringContentStorage.createFromReader(reader); } } else { storage = BytesContentStorage.createFromStream(is, contentFile.length(), fileCharset); } //StringContentStorage. contentDetached = content.updateContents(localMonitor, storage); } catch (IOException e) { throw new DBException("Error reading content from file", e); } } else { // Create new storage and pass it to content storage = new TemporaryContentStorage(DBeaverCore.getInstance(), contentFile, fileCharset); contentDetached = content.updateContents(localMonitor, storage); } } @Nullable @Override public DBCExecutionContext getExecutionContext() { return valueController.getExecutionContext(); } public String getEncoding() { return fileCharset; } @Override public String getDefaultEncoding() { return DBValueFormatting.getDefaultBinaryFileEncoding(valueController.getExecutionContext().getDataSource()); } public void setEncoding(String fileCharset) { this.fileCharset = fileCharset; for (IEditorPart part : editorParts) { try { part.init(part.getEditorSite(), this); } catch (PartInitException e) { log.error("Error refreshing content editor part " + part, e); } } } }