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.commons.io.res.type.ram;
020
021import java.io.ByteArrayInputStream;
022import java.io.ByteArrayOutputStream;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.OutputStream;
026
027import lucee.commons.io.ModeUtil;
028import lucee.commons.io.res.ContentType;
029import lucee.commons.io.res.Resource;
030import lucee.commons.io.res.ResourceProvider;
031import lucee.commons.io.res.util.ResourceSupport;
032import lucee.commons.io.res.util.ResourceUtil;
033import lucee.commons.lang.StringUtil;
034
035
036/**
037 * a ram resource
038 */
039public final class RamResource extends ResourceSupport {
040        
041        private final RamResourceProviderOld provider;
042        
043        private final String parent;
044        private final String name;
045        private RamResourceCore _core;
046
047        RamResource(RamResourceProviderOld provider, String path) {
048                this.provider=provider;
049                if(path.equals("/") || StringUtil.isEmpty(path)) {
050                //if(path.equals("/")) {
051                        this.parent=null;
052                        this.name="";
053                }
054                else {
055                        String[] pn = ResourceUtil.translatePathName(path);
056                        this.parent=pn[0];
057                        this.name=pn[1];
058                }
059                
060        }
061        
062        private RamResource(RamResourceProviderOld provider, String parent,String name) {
063                this.provider=provider;
064                this.parent=parent ;
065                this.name=name;
066        }
067        
068        RamResourceCore getCore() {
069                if(_core==null || _core.getType()==0) {
070                        _core=provider.getCore(getInnerPath());
071                }
072                return _core;
073        }
074        
075
076        void removeCore() {
077                if(_core==null)return;
078                _core.remove();
079                _core=null;
080        }
081        
082        private RamResourceCore createCore(int type) throws IOException {
083                return _core=provider.createCore(getInnerPath(),type);
084        }
085        
086
087        @Override
088        public String getPath() {
089                return provider.getScheme().concat("://").concat(getInnerPath());
090        }
091        private String getInnerPath() {
092                if(parent==null) return "/";
093                return parent.concat(name);
094        }
095
096        @Override
097        public String getName() {
098                return name;
099        }
100
101        @Override
102        public String getParent() {
103                if(isRoot()) return null;
104                return provider.getScheme().concat("://").concat(ResourceUtil.translatePath(parent, true, false));
105        }
106
107        @Override
108        public boolean isReadable() {
109                return ModeUtil.isReadable(getMode());
110        }
111
112        @Override
113        public boolean isWriteable() {
114                return ModeUtil.isWritable(getMode());
115        }
116
117        @Override
118        public void remove(boolean force) throws IOException {
119                if(isRoot()) 
120                        throw new IOException("can't remove root resource ["+getPath()+"]");
121
122                provider.read(this);
123                RamResourceCore core = getCore();
124                if(core==null)
125                        throw new IOException("can't remove resource ["+getPath()+"],resource does not exist");
126                
127                Resource[] children = listResources();
128                if(children!=null && children.length>0) {
129                        if(!force) {
130                                throw new IOException("can't delete directory ["+getPath()+"], directory is not empty");
131                        }
132                        for(int i=0;i<children.length;i++) {
133                                children[i].remove(true);
134                        }
135                }
136                removeCore();
137        }
138
139        @Override
140        public boolean exists() {
141                try {
142                        provider.read(this);
143                } catch (IOException e) {
144                        return true;
145                }
146                return getCore()!=null;
147        }
148
149        @Override
150        public Resource getParentResource() {
151                return getParentRamResource();
152        }
153
154
155        private RamResource getParentRamResource() {
156                if(isRoot()) return null;
157                return new RamResource(provider,parent);
158        }
159
160        public Resource getRealResource(String realpath) {
161                realpath=ResourceUtil.merge(getInnerPath(), realpath);
162                if(realpath.startsWith("../"))return null;
163
164                return new RamResource(provider,realpath);
165        }
166
167        @Override
168        public boolean isAbsolute() {
169                return true;
170        }
171
172        @Override
173        public boolean isDirectory() {
174                return exists() && getCore().getType()==RamResourceCore.TYPE_DIRECTORY;
175        }
176
177        @Override
178        public boolean isFile() {
179                return exists() && getCore().getType()==RamResourceCore.TYPE_FILE;
180        }
181
182        @Override
183        public long lastModified() {
184                if(!exists()) return 0;
185                return getCore().getLastModified();
186        }
187
188        @Override
189        public long length() {
190                if(!exists()) return 0;
191                byte[] data= getCore().getData();
192                if(data==null) return 0;
193                return data.length;
194        }
195
196        @Override
197        public String[] list() {
198                if(!exists()) return null;
199                RamResourceCore core = getCore();
200                if(core.getType()!=RamResourceCore.TYPE_DIRECTORY)
201                        return null;
202                
203                return core.getChildNames();
204                /*List list = core.getChildren();
205                if(list==null && list.size()==0) return new String[0];
206                
207                Iterator it = list.iterator();
208                String[] children=new String[list.size()];
209                RamResourceCore cc;
210                int count=0;
211                while(it.hasNext()) {
212                        cc=(RamResourceCore) it.next();
213                        children[count++]=cc.getName();
214                }
215                return children;*/
216        }
217
218        @Override
219        public Resource[] listResources() {
220                String[] list = list();
221                if(list==null)return null;
222                
223                Resource[] children=new Resource[list.length];
224                String p=getInnerPath();
225                if(!isRoot())p=p.concat("/");
226                for(int i=0;i<children.length;i++) {
227                        children[i]=new RamResource(provider,p,list[i]);
228                }
229                return children;
230        }
231
232        @Override
233        public boolean setLastModified(long time) {
234                if(!exists()) return false;
235                getCore().setLastModified(time);
236                return true;
237        }
238
239        @Override
240        public boolean setReadOnly() {
241                return setWritable(false);
242        }
243
244        @Override
245        public void createFile(boolean createParentWhenNotExists) throws IOException {
246                ResourceUtil.checkCreateFileOK(this,createParentWhenNotExists);
247                provider.lock(this);
248                try {
249                        createCore(RamResourceCore.TYPE_FILE);
250                }
251                finally {
252                        provider.unlock(this);
253                }
254        }
255
256
257        @Override
258        public void createDirectory(boolean createParentWhenNotExists) throws IOException {
259                ResourceUtil.checkCreateDirectoryOK(this,createParentWhenNotExists);
260                provider.lock(this);
261                try {
262                        createCore(RamResourceCore.TYPE_DIRECTORY);
263                }
264                finally {
265                        provider.unlock(this);
266                }
267                
268        }
269
270        @Override
271        public InputStream getInputStream() throws IOException {
272                ResourceUtil.checkGetInputStreamOK(this);
273
274                provider.lock(this);
275                RamResourceCore core = getCore();
276                
277                byte[] data = core.getData();
278                if(data==null)data=new byte[0];
279                provider.unlock(this);
280                return new ByteArrayInputStream(data);
281        }
282
283        public OutputStream getOutputStream(boolean append) throws IOException {
284                ResourceUtil.checkGetOutputStreamOK(this);
285                provider.lock(this);
286                return new RamOutputStream(this,append);
287        }
288
289        public ContentType getContentType() {
290                return ResourceUtil.getContentType(this);
291        }
292
293        @Override
294        public ResourceProvider getResourceProvider() {
295                return provider;
296        }
297        @Override
298        public String toString() {
299                return getPath();
300        }
301        
302
303        /**
304         * This is useed by the MemoryResource too write back data to, that are written to outputstream
305         */
306        class RamOutputStream extends ByteArrayOutputStream {
307
308                private RamResource res;
309                private boolean append;
310
311                /**
312                 * Constructor of the class
313                 * @param res
314                 */
315                public RamOutputStream(RamResource res, boolean append) {
316                        this.append=append;
317                        this.res=res;
318                }
319
320                @Override
321                public void close() throws IOException {
322                        try {
323                                super.close();
324                                RamResourceCore core = res.getCore();
325                                if(core==null)core=res.createCore(RamResourceCore.TYPE_FILE);
326                                
327                                core.setData(this.toByteArray(),append);
328                        }
329                        finally {
330                                res.getResourceProvider().unlock(res);
331                        }
332                }
333        }
334
335        @Override
336        public boolean setReadable(boolean value) {
337                if(!exists())return false;
338                try {
339                        setMode(ModeUtil.setReadable(getMode(), value));
340                        return true;
341                } catch (IOException e) {
342                        return false;
343                }
344                
345        }
346        
347        @Override
348        public boolean setWritable(boolean value) {
349                if(!exists())return false;
350                try {
351                        setMode(ModeUtil.setWritable(getMode(), value));
352                        return true;
353                } catch (IOException e) {
354                        return false;
355                }
356        }
357
358        private boolean isRoot() {
359                return parent==null;
360        }
361        
362        public int getMode() {
363                if(!exists())return 0;
364                return getCore().getMode();
365        }
366        
367        public void setMode(int mode) throws IOException {
368                if(!exists())throw new IOException("can't set mode on resource ["+this+"], resource does not exist");
369                getCore().setMode(mode);
370        }
371        @Override
372        public boolean getAttribute(short attribute) {
373                if(!exists())return false;
374                return (getCore().getAttributes()&attribute)>0;
375        }
376        @Override
377        public void setAttribute(short attribute, boolean value) throws IOException {
378                if(!exists())throw new IOException("can't get attributes on resource ["+this+"], resource does not exist");
379                int attr = getCore().getAttributes();
380                if(value) {
381                        if((attr&attribute)==0) attr+=attribute;
382                }
383                else {
384                        if((attr&attribute)>0) attr-=attribute;
385                }
386                getCore().setAttributes(attr);
387        }
388        
389}