001    package railo.commons.io.res.type.datasource;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.ByteArrayOutputStream;
005    import java.io.IOException;
006    import java.io.InputStream;
007    import java.io.OutputStream;
008    
009    import railo.commons.io.IOUtil;
010    import railo.commons.io.ModeUtil;
011    import railo.commons.io.res.Resource;
012    import railo.commons.io.res.ResourceProvider;
013    import railo.commons.io.res.type.datasource.DatasourceResourceProvider.ConnectionData;
014    import railo.commons.io.res.util.ResourceSupport;
015    import railo.commons.io.res.util.ResourceUtil;
016    import railo.commons.lang.StringUtil;
017    import railo.runtime.exp.PageException;
018    import railo.runtime.exp.PageRuntimeException;
019    import railo.runtime.type.util.ArrayUtil;
020    
021    public final class DatasourceResource extends ResourceSupport {
022    
023            
024    
025            private DatasourceResourceProvider provider;
026            private String parent;
027            private String name;
028            private ConnectionData data;
029            private int fullPathHash;
030            private int pathHash;
031            
032            /**
033             * Constructor of the class
034             * @param provider
035             * @param data
036             * @param path
037             */
038            DatasourceResource(DatasourceResourceProvider provider, ConnectionData data,String path) {
039                    this.provider=provider;
040                    this.data=data;
041                    if("/".equals(path)) {
042                            this.parent=null;
043                            this.name="";
044                    }
045                    else {
046                            String[] pn = ResourceUtil.translatePathName(path);
047                            this.parent=pn[0];
048                            this.name=pn[1];
049                    }
050            }
051    
052    
053            private int fullPathHash() {
054                    if(fullPathHash==0) fullPathHash=getInnerPath().hashCode();
055                    return fullPathHash;
056            }
057            
058            private int pathHash() {
059                    if(pathHash==0 && parent!=null) pathHash=parent.hashCode();
060                    return pathHash;
061            }
062    
063            private Attr attr() {
064                    return provider.getAttr(data,fullPathHash(),parent,name);
065            }
066    
067    
068            private boolean isRoot() {
069                    return parent==null;
070            }
071            
072            /**
073             * @see railo.commons.io.res.Resource#createDirectory(boolean)
074             */
075            public void createDirectory(boolean createParentWhenNotExists) throws IOException {
076                    ResourceUtil.checkCreateDirectoryOK(this,createParentWhenNotExists);
077                    provider.create(data,fullPathHash(),pathHash(),parent,name,Attr.TYPE_DIRECTORY);
078                    
079            }
080    
081            /**
082             * @see railo.commons.io.res.Resource#createFile(boolean)
083             */
084            public void createFile(boolean createParentWhenNotExists) throws IOException {
085                    ResourceUtil.checkCreateFileOK(this,createParentWhenNotExists);
086                    provider.create(data,fullPathHash(),pathHash(),parent,name,Attr.TYPE_FILE);
087            }
088            
089            /**
090             * @see railo.commons.io.res.Resource#remove(boolean)
091             */
092            public void remove(boolean force) throws IOException {
093                    ResourceUtil.checkRemoveOK(this);
094                    if(isRoot()) 
095                            throw new IOException("can't remove root resource ["+getPath()+"]");
096    
097                    
098                    Resource[] children = listResources();
099                    if(children!=null && children.length>0) {
100                            if(!force) {
101                                    throw new IOException("can't delete directory ["+getPath()+"], directory is not empty");
102                            }
103                            for(int i=0;i<children.length;i++) {
104                                    children[i].remove(true);
105                            }
106                    }
107                    provider.delete(data,fullPathHash(),parent,name);
108            }
109    
110            /**
111             * @see railo.commons.io.res.Resource#exists()
112             */
113            public boolean exists() {
114                    return attr().exists();
115            }
116    
117            /**
118             * @see railo.commons.io.res.Resource#getInputStream()
119             */
120            public InputStream getInputStream() throws IOException {
121                    ResourceUtil.checkGetInputStreamOK(this);
122                    return provider.getInputStream(data,fullPathHash(),parent,name);
123            }
124    
125            /**
126             * @see railo.commons.io.res.Resource#getMode()
127             */
128            public int getMode() {
129                    return attr().getMode();
130            }
131    
132            /**
133             * @see res.Resource#getFullName()
134             */
135            public String getName() {
136                    return name;
137            }
138    
139            /**
140             * @see railo.commons.io.res.Resource#getOutputStream(boolean)
141             */
142            public OutputStream getOutputStream(boolean append) throws IOException {
143                    ResourceUtil.checkGetOutputStreamOK(this);
144                    byte[] barr=null;
145                    
146                    if(append && !provider.concatSupported(data) && isFile()){
147                            try{
148                                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
149                                    IOUtil.copy(getInputStream(), baos,true,true);
150                                    barr = baos.toByteArray();
151                            }
152                            catch(Throwable t){
153                                    
154                            }
155                    }
156                    
157                    OutputStream os = provider.getOutputStream(data,fullPathHash(),pathHash(),parent,name,append);
158                    if(!ArrayUtil.isEmpty(barr))IOUtil.copy(new ByteArrayInputStream(barr), os,true,false);
159                    return os;
160            }
161    
162            /**
163             * @see railo.commons.io.res.Resource#getParent()
164             */
165            public String getParent() {
166                    if(isRoot()) return null;
167                    String p = (StringUtil.isEmpty(parent))?"/":parent;
168                    return provider.getScheme().concat("://").concat(data.key()).concat(ResourceUtil.translatePath(p, true, false));
169                    
170            }
171    
172            /**
173             * @see railo.commons.io.res.Resource#getParentResource()
174             */
175            public Resource getParentResource() {
176                    return getParentDatasourceResource();
177            }
178            private DatasourceResource getParentDatasourceResource() {
179                    if(isRoot()) return null;
180                    return new DatasourceResource(provider,data,parent);
181            }
182    
183            /**
184             * @see res.Resource#getPath()
185             */
186            public String getPath() {
187                    return provider.getScheme().concat("://").concat(data.key()).concat(getInnerPath());
188            }
189            private String getInnerPath() {
190                    if(parent==null) return "/";
191                    return parent.concat(name);
192            }
193    
194            /**
195             * @see railo.commons.io.res.Resource#getRealResource(java.lang.String)
196             */
197            public Resource getRealResource(String realpath) {
198                    realpath=ResourceUtil.merge(getInnerPath(), realpath);
199                    if(realpath.startsWith("../"))return null;
200    
201                    return new DatasourceResource(provider,data,realpath);
202            }
203    
204            /**
205             * @see railo.commons.io.res.Resource#getResourceProvider()
206             */
207            public ResourceProvider getResourceProvider() {
208                    return provider;
209            }
210    
211            /**
212             * @see railo.commons.io.res.Resource#isAbsolute()
213             */
214            public boolean isAbsolute() {
215                    return true;
216            }
217    
218            /**
219             * @see railo.commons.io.res.Resource#isDirectory()
220             */
221            public boolean isDirectory() {
222                    return attr().isDirectory();
223            }
224            
225            /**
226             * @see railo.commons.io.res.Resource#isFile()
227             */
228            public boolean isFile() {
229                    return attr().isFile();
230            }
231    
232            /**
233             * @see railo.commons.io.res.Resource#isReadable()
234             */
235            public boolean isReadable() {
236                    return ModeUtil.isReadable(getMode());
237            }
238    
239            /**
240             * @see railo.commons.io.res.Resource#isWriteable()
241             */
242            public boolean isWriteable() {
243                    return ModeUtil.isWritable(getMode());
244            }
245    
246            /**
247             * @see railo.commons.io.res.Resource#lastModified()
248             */
249            public long lastModified() {
250                    return attr().getLastModified();
251            }
252    
253            /**
254             * @see railo.commons.io.res.Resource#length()
255             */
256            public long length() {
257                    return attr().size();
258            }
259    
260            /**
261             * @see railo.commons.io.res.Resource#listResources()
262             */
263            public Resource[] listResources() {
264                    if(!attr().isDirectory())return null;
265                    
266                    String path;
267                    if(parent==null) path= "/";
268                    else path=parent.concat(name).concat("/");
269                    
270                    
271                    Attr[] children=null;
272                    try {
273                            children = provider.getAttrs(data,path.hashCode(),path);
274                    } catch (PageException e) {
275                            throw new PageRuntimeException(e);
276                    }
277                    if(children==null) return new Resource[0];
278                    Resource[] attrs = new Resource[children.length];
279                    for(int i=0;i<children.length;i++) {
280                            // TODO optimieren, alle attr mitgeben
281                            attrs[i]=new DatasourceResource(provider,data,path+children[i].getName());
282                    }
283                    return attrs;
284            }
285    
286            /**
287             * @see railo.commons.io.res.Resource#setLastModified(long)
288             */
289            public boolean setLastModified(long time) {
290                    if(!exists()) return false;
291                    return provider.setLastModified(data,fullPathHash(),parent,name,time);
292            }
293    
294            /**
295             * @see railo.commons.io.res.Resource#setMode(int)
296             */
297            public void setMode(int mode) throws IOException {
298                    if(!exists())throw new IOException("can't set mode on resource ["+this+"], resource does not exists");
299                    provider.setMode(data,fullPathHash(),parent,name,mode);
300            }
301    
302    
303            /**
304             * @see railo.commons.io.res.util.ResourceSupport#moveTo(railo.commons.io.res.Resource)
305             */
306            public void moveTo(Resource dest) throws IOException {
307                    super.moveTo(dest);// TODO
308            }
309            
310            
311            /**
312             * @see railo.commons.io.res.Resource#setReadable(boolean)
313             */
314            public boolean setReadable(boolean readable) {
315                    if(!exists())return false;
316                    try {
317                            setMode(ModeUtil.setReadable(getMode(), readable));
318                            return true;
319                    } catch (IOException e) {
320                            return false;
321                    }
322            }
323    
324            /**
325             * @see railo.commons.io.res.Resource#setWritable(boolean)
326             */
327            public boolean setWritable(boolean writable) {
328                    if(!exists())return false;
329                    try {
330                            setMode(ModeUtil.setWritable(getMode(), writable));
331                            return true;
332                    } catch (IOException e) {
333                            return false;
334                    }
335            }
336            
337            /**
338             * @see java.lang.Object#toString()
339             */
340            public String toString() {
341                    return getPath();
342            }
343    
344    }