001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.functions.file;
020
021import java.io.BufferedOutputStream;
022import java.io.File;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.RandomAccessFile;
026
027import lucee.commons.io.IOUtil;
028import lucee.commons.io.res.Resource;
029import lucee.runtime.exp.PageException;
030import lucee.runtime.op.Caster;
031import lucee.runtime.op.Decision;
032
033public class FileStreamWrapperWrite extends FileStreamWrapper {
034        
035        private BufferedOutputStream bos;
036        private boolean append;
037        private String charset;
038        private boolean seekable;
039        private RandomAccessFile raf;
040
041        public FileStreamWrapperWrite(Resource res, String charset,boolean append,boolean seekable) {
042                super(res);
043                
044                this.charset=charset;
045                this.append=append;
046                this.seekable=seekable;
047        }
048        
049
050        @Override
051        public void write(Object obj) throws IOException {
052                byte[] bytes = null;
053                InputStream is=null;
054                if(Decision.isBinary(obj)){
055                        bytes=Caster.toBinary(obj,null);
056                }
057                else if(obj instanceof FileStreamWrapper) {
058                        is=((FileStreamWrapper)obj).getResource().getInputStream();
059                }
060                else if(obj instanceof Resource) {
061                        is=((Resource)obj).getInputStream();
062                }
063                else {//if(Decision.isSimpleValue(obj)){
064                        String str = Caster.toString(obj,false,null);
065                        if(str!=null) bytes=str.getBytes(charset);
066                }
067                
068                if(bytes!=null){
069                        if(seekable)getRAF().write(bytes);
070                        else _getOS().write(bytes);
071                }
072                else if(is!=null){
073                        if(seekable)writeToRAF(is, getRAF());
074                        else IOUtil.copy(is, _getOS(),true,false);
075                }
076                else
077                        throw new IOException("can't write down object of type ["+Caster.toTypeName(obj)+"] to resource ["+res+"]");
078                
079                
080                
081                
082        }
083
084        public void close() throws IOException {
085                if(bos!=null)bos.close();
086                if(raf!=null)raf.close();
087        }
088
089        @Override
090        public String getMode() {
091                return append?"append":"write";
092        }
093        
094        @Override
095        public void skip(int len) throws PageException {
096                if(seekable){
097                        try {
098                                getRAF().skipBytes(len);
099                        } catch (IOException e) {
100                                throw Caster.toPageException(e);
101                        }
102                }       
103                else throw Caster.toPageException(new IOException("skip is only supported when you have set argument seekable of function fileOpen to true"));
104        }
105        public void seek(long pos) throws PageException {
106                if(seekable){
107                        try {
108                                getRAF().seek(pos);
109                        } catch (IOException e) {
110                                throw Caster.toPageException(e);
111                        }
112                }
113                else throw Caster.toPageException(new IOException("seek is only supported when you have set argument seekable of function fileOpen to true"));
114        }
115        
116        public static void writeToRAF(InputStream is, RandomAccessFile raf) throws IOException   {  
117        
118        byte[] buffer = new byte[2048];  
119        int tmp=0;  
120   
121        while ((tmp = is.read(buffer)) != -1)   {  
122          raf.write(buffer, 0, tmp);  
123        }   
124    } 
125        
126        private RandomAccessFile getRAF() throws IOException {
127                if(raf==null){
128                        if(!(res instanceof File))
129                                throw new IOException("only resources for local filesytem support seekable");
130                        
131                        raf = new RandomAccessFile((File)res,"rw");
132                        if(append)raf.seek(res.length());
133                }
134                return raf;
135        }
136
137        private BufferedOutputStream _getOS() throws IOException{
138                if(bos==null)
139                        bos = IOUtil.toBufferedOutputStream(res.getOutputStream(append));
140                return bos;
141        }
142}