/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.ctp.stream;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import divconq.ctp.f.FileDescriptor;
import divconq.filestore.IFileStoreDriver;
import divconq.filestore.IFileStoreFile;
import divconq.filestore.local.FileSystemFile;
import divconq.lang.op.OperationContext;
import divconq.script.StackEntry;
import divconq.struct.Struct;
import divconq.struct.scalar.NullStruct;
import divconq.xml.XElement;
public class FileDestStream extends BaseStream implements IStreamDest {
protected FileSystemFile file = null;
protected FileChannel out = null;
protected boolean userelpath = false;
protected String relpath = "";
public FileDestStream(FileSystemFile file) {
this.file = file;
}
public FileDestStream withRelative(boolean v) {
this.userelpath = v;
return this;
}
// for use with dcScript
@Override
public void init(StackEntry stack, XElement el, boolean autorelative) {
if (autorelative || stack.boolFromElement(el, "Relative", false) || el.getName().startsWith("X")) {
this.userelpath = true;
}
Struct src = stack.refFromElement(el, "RelativeTo");
if ((src != null) && !(src instanceof NullStruct)) {
if (src instanceof IFileStoreDriver)
this.relpath = "";
else if (src instanceof IFileStoreFile)
this.relpath = ((IFileStoreFile)src).getPath();
else
this.relpath = src.toString();
this.userelpath = true;
}
}
@Override
public void close() {
//System.out.println("File DEST killed"); // TODO
if (this.out != null)
try {
this.out.close();
}
catch (IOException x) {
}
this.out = null;
this.file = null;
super.close();
}
@Override
public ReturnOption handle(FileDescriptor file, ByteBuf data) {
if (file == FileDescriptor.FINAL) {
OperationContext.get().getTaskRun().complete();
return ReturnOption.DONE;
}
if (this.file.isFolder())
return this.handleLocalFolder(file, data);
return this.handleLocalFile(file, data);
}
public ReturnOption handleLocalFile(FileDescriptor file, ByteBuf data) {
if (file.isFolder()) {
if (data != null)
data.release();
OperationContext.get().getTaskRun().kill("Folder cannot be stored into a file");
return ReturnOption.DONE;
}
if (data != null) {
if (this.out == null) {
try {
Path dpath = this.file.localPath();
Files.createDirectories(dpath.getParent());
this.out = FileChannel.open(dpath,
StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC);
}
catch (IOException x) {
if (data != null)
data.release();
OperationContext.get().getTaskRun().kill("Problem opening destination file: " + x);
return ReturnOption.DONE;
}
}
for (ByteBuffer buff : data.nioBuffers()) {
try {
this.out.write(buff);
}
catch (IOException x) {
data.release();
OperationContext.get().getTaskRun().kill("Problem writing destination file: " + x);
return ReturnOption.DONE;
}
}
data.release();
}
if (file.isEof()) {
try {
if (this.out != null) {
this.out.close();
this.out = null;
}
this.file.refreshProps();
}
catch (IOException x) {
OperationContext.get().getTaskRun().kill("Problem closing destination file: " + x);
return ReturnOption.DONE;
}
}
return ReturnOption.CONTINUE;
}
public ReturnOption handleLocalFolder(FileDescriptor file, ByteBuf data) {
Path folder = this.file.localPath();
if (Files.notExists(folder))
try {
Files.createDirectories(folder);
}
catch (IOException x) {
if (data != null)
data.release();
OperationContext.get().getTaskRun().kill("Problem making destination top folder: " + x);
return ReturnOption.DONE;
}
String fpath = (this.userelpath) ? this.relpath + file.getPath() : "/" + file.path().getFileName();
if (file.isFolder()) {
try {
Files.createDirectories(folder.resolve(fpath.substring(1)));
}
catch (IOException x) {
if (data != null)
data.release();
OperationContext.get().getTaskRun().kill("Problem making destination folder: " + x);
return ReturnOption.DONE;
}
return ReturnOption.CONTINUE;
}
if (this.out == null)
try {
Path dpath = folder.resolve(fpath.substring(1));
Files.createDirectories(dpath.getParent());
this.out = FileChannel.open(dpath,
StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC);
}
catch (IOException x) {
if (data != null)
data.release();
OperationContext.get().getTaskRun().kill("Problem opening destination file: " + x);
return ReturnOption.DONE;
}
if (data != null) {
for (ByteBuffer buff : data.nioBuffers()) {
try {
this.out.write(buff);
}
catch (IOException x) {
data.release();
OperationContext.get().getTaskRun().kill("Problem writing destination file: " + x);
return ReturnOption.DONE;
}
}
data.release();
}
if (file.isEof()) {
try {
this.out.close();
this.out = null;
this.file.refreshProps();
}
catch (IOException x) {
OperationContext.get().getTaskRun().kill("Problem closing destination file: " + x);
return ReturnOption.DONE;
}
}
return ReturnOption.CONTINUE;
}
@Override
public void read() {
// we are terminal, no downstream should call us
OperationContext.get().getTaskRun().kill("File destination cannot be a source");
}
@Override
public void execute() {
// TODO optimize if upstream is local file also
this.upstream.read();
}
}