001    package railo.commons.io;
002    
003    import java.io.IOException;
004    
005    import railo.commons.io.res.Resource;
006    import railo.commons.lang.StringUtil;
007    import railo.runtime.SourceFile;
008    import railo.runtime.type.Array;
009    import railo.runtime.type.List;
010    
011    /**
012     * represent a cfml file on the runtime system
013     */
014    public final class SourceFileImpl implements SourceFile {
015    
016            private final Resource root;
017            private Resource file;
018            private String realpath;
019            private boolean isOutSide=false;
020            private String name;
021            private String className;
022            private String packageName;
023            private String javaName;
024        private boolean trusted;
025        private boolean hasArchive;
026            
027            
028            /**
029             * constructor of the class
030             * @param root rott directory of the file
031             * @param realPath relative path to root directory
032             */
033            public SourceFileImpl(Resource root, String realPath) {
034                    
035                    this.root=root;
036                    
037                    this.realpath=realPath.replace('\\','/');
038                    
039                    if(realPath.indexOf('/')==0) {
040                            
041                    }
042                    else if(realPath.startsWith("../")) {
043                            isOutSide=true;
044                    }
045                    else if(realPath.startsWith("./")) {
046                            this.realpath=this.realpath.substring(1);
047                    }
048                    else {
049                            this.realpath="/"+this.realpath;
050                    }
051            }
052            
053            /**
054             * constructor of the class
055             * @param parent parent sourceFile
056             * @param realPath relapath to parent sourc file (not root)
057             */
058            public SourceFileImpl(SourceFileImpl parent, String realPath) {
059                    realpath=realPath.replace('\\','/');
060                    if(realPath.equals(".") || realPath.equals(".."))realPath+="/";
061                    this.root=parent.root;
062    
063                    name=parent.name;
064                    trusted=parent.trusted;
065                    hasArchive=parent.hasArchive;
066                    isOutSide=parent.isOutSide;
067    
068                    if(realPath.indexOf('/')==0) {
069                            isOutSide=false;
070                            this.realpath=realPath;
071                    }
072                    else if(realPath.indexOf("./")==0) {
073                            this.realpath=mergeRealPathes(parent.getRealpath(), realPath.substring(2));
074                    }
075                    else {
076                            this.realpath=mergeRealPathes(parent.getRealpath(), realPath);
077                    }
078            }
079            
080            /**
081             * merge to realpath to one
082             * @param parentRealPath 
083             * @param newRealPath
084             * @return merged realpath
085             */
086            private String mergeRealPathes(String parentRealPath, String newRealPath) {
087                    // remove file from parent
088                    parentRealPath=pathRemoveLast(parentRealPath);
089                    
090                    while(newRealPath.startsWith("../")) {
091                            parentRealPath=pathRemoveLast(parentRealPath);
092                            newRealPath=newRealPath.substring(3);
093                    }
094                    // check if come back
095                    String path=parentRealPath+"/"+newRealPath;
096                    
097                    if(path.startsWith("../")) {
098                            int count=0;
099                            while(path.startsWith("../")) {
100                                    count++;
101                                    path=path.substring(3);
102                            }
103                            
104                            String strRoot=root.getAbsolutePath().replace('\\','/');
105                            if(strRoot.lastIndexOf('/')!=strRoot.length()-1) {
106                                    strRoot+='/';
107                            }
108                            int rootLen=strRoot.length();
109                            String[] arr=path.split("/");
110                            for(int i=count;i>0;i--) {
111                                    if(arr.length>i) {
112                                            String tmp="/"+list(arr,0,i);
113                                            if(strRoot.lastIndexOf(tmp)==rootLen-tmp.length()) {
114                                                    StringBuilder rtn=new StringBuilder();
115                                                    for(int y=0;i<count-i;y++) rtn.append("../");
116                                                    isOutSide=rtn.length()!=0;
117                                                    return rtn.toString()+(rtn.length()==0?"/":"")+list(arr,i,arr.length);
118                                            }
119                                    }
120                            }
121                            //System.out.println(strRoot+" - "+path);
122                    }
123                    
124                    return parentRealPath+"/"+newRealPath;
125            }
126    
127            /**
128             * convert a String array to a string list, but only part of it 
129             * @param arr String Array
130             * @param from start from here
131             * @param len how many element
132             * @return String list
133             */
134            private String list(String[] arr,int from, int len) {
135                    StringBuilder sb=new StringBuilder();
136                    for(int i=from;i<len;i++) {
137                            sb.append(arr[i]);
138                            if(i+1!=arr.length)sb.append('/');
139                    }
140                    return sb.toString();
141            }
142    
143            
144            
145            /**
146             * remove the last elemtn of a path
147             * @param path path to remove last element from it
148             * @return path with removed element
149             */
150            private String pathRemoveLast(String path) {
151                    if(path.length()==0) {
152                            isOutSide=true;
153                            return "..";
154                    }
155                    else if(path.lastIndexOf("..")==path.length()-2){
156                            isOutSide=true;
157                            return path+"/..";
158                    }
159                    return path.substring(0,path.lastIndexOf('/'));
160            }
161            
162            /**
163             * @see railo.runtime.SourceFile#getFile()
164             */
165            public Resource getFile() {
166                    if(file==null) {
167                            if(isOutSide) {
168                                    try {
169                                            file=root.getRealResource(realpath).getCanonicalResource();
170                                    } catch (IOException e) {
171                                    }
172                            }
173                            file=root.getRealResource(realpath);
174                    }
175                    return file;
176            }
177            
178            /**
179             * @return Returns the realpath.
180             */
181            public String getRealpath() {
182                    return realpath;
183            }
184            /**
185             * @return Returns the root.
186             */
187            public Resource getRoot() {
188                    return root;
189            }
190            
191            /**
192             * @return returns a variable string based on realpath and return it
193             */
194            public String getRealPathAsVariableString() {
195                    return StringUtil.toIdentityVariableName(getRealpath());
196                    //return StringUtil.toClassName(getRealpath());
197            }
198            
199            /**
200             * @return returns the a classname matching to filename
201             */
202            public String getClassName() {
203                    if(className==null) createClassAndPackage();
204                    return className;
205            }
206            
207            /**
208             * @return returns the java name
209             */
210            public String getJavaName() {
211                    if(javaName==null) createClassAndPackage();
212                    return javaName;
213            }
214            
215            /**
216             * @return returns the a package matching to file
217             */
218            public String getPackageName() {
219                    if(packageName==null) createClassAndPackage();
220                    return packageName;
221            }
222            
223            private void createClassAndPackage() {
224                    String str=realpath;
225                    StringBuilder packageName=new StringBuilder();
226                    StringBuilder javaName=new StringBuilder();
227                    while(str.indexOf('/')==0)str=str.substring(1);
228                    while(str.lastIndexOf('/')==str.length()-1)str=str.substring(0,str.length()-1);
229                    
230                    //String[] arr=str.split("/");
231                    Array arr = List.listToArray(str, '/');
232                    int len=arr.size();
233                    String value;
234                    for(int i=1;i<=len;i++) {
235                            value=(String) arr.get(i,"");
236                            String varName=StringUtil.toVariableName(value);
237                            javaName.append("/"+varName);
238                            if(i==len) {
239                                    className=varName.toLowerCase();
240                            }
241                            else {
242                                    if(i!=0) packageName.append('.');
243                                    packageName.append(varName);
244                            }
245                    }
246                    this.packageName=packageName.toString().toLowerCase();
247                    this.javaName=javaName.toString().toLowerCase();
248            }
249            
250            
251    
252            /**
253             * @return returns a variable string based on root and return it
254             */
255            public String getRootPathAsVariableString() {
256                    return StringUtil.toIdentityVariableName(root.getAbsolutePath());
257            }
258            
259        /**
260         * @return has context a archive or not
261         */
262        public boolean hasArchive() {
263            return hasArchive;
264        }
265        
266        /**
267         * @return returns if is trusted or not
268         */
269        public boolean isTrusted() {
270            return trusted;
271        }
272    
273        /**
274         * @see railo.runtime.SourceFile#getPhyscalFile()
275         */
276        public Resource getPhyscalFile() {
277            return getFile();
278        }
279    
280        /**
281         * @see railo.runtime.SourceFile#getDisplayPath()
282         */
283        public String getDisplayPath() {
284            return getFile().getAbsolutePath();
285        }
286    
287            /**
288             * @see railo.runtime.SourceFile#getFullClassName()
289             */
290            public String getFullClassName() {
291                    String p=getPackageName();
292                    if(p.length()==0) return getClassName();
293                    return p.concat(".").concat(getClassName());
294            }
295    }