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.util.ListUtil;
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                                                    while(i<count-i) {
116                                                            count--;
117                                                            rtn.append("../");
118                                                    }
119                                                    isOutSide=rtn.length()!=0;
120                                                    return rtn.toString()+(rtn.length()==0?"/":"")+list(arr,i,arr.length);
121                                            }
122                                    }
123                            }
124                            //System.out.println(strRoot+" - "+path);
125                    }
126                    
127                    return parentRealPath+"/"+newRealPath;
128            }
129    
130            /**
131             * convert a String array to a string list, but only part of it 
132             * @param arr String Array
133             * @param from start from here
134             * @param len how many element
135             * @return String list
136             */
137            private String list(String[] arr,int from, int len) {
138                    StringBuilder sb=new StringBuilder();
139                    for(int i=from;i<len;i++) {
140                            sb.append(arr[i]);
141                            if(i+1!=arr.length)sb.append('/');
142                    }
143                    return sb.toString();
144            }
145    
146            
147            
148            /**
149             * remove the last elemtn of a path
150             * @param path path to remove last element from it
151             * @return path with removed element
152             */
153            private String pathRemoveLast(String path) {
154                    if(path.length()==0) {
155                            isOutSide=true;
156                            return "..";
157                    }
158                    else if(path.lastIndexOf("..")==path.length()-2){
159                            isOutSide=true;
160                            return path+"/..";
161                    }
162                    return path.substring(0,path.lastIndexOf('/'));
163            }
164            
165            public Resource getFile() {
166                    return getResource();
167            }
168            
169            @Override
170            public Resource getResource() {
171                    if(file==null) {
172                            if(isOutSide) {
173                                    try {
174                                            file=root.getRealResource(realpath).getCanonicalResource();
175                                    } catch (IOException e) {
176                                    }
177                            }
178                            file=root.getRealResource(realpath);
179                    }
180                    return file;
181            }
182            
183            /**
184             * @return Returns the realpath.
185             */
186            public String getRealpath() {
187                    return realpath;
188            }
189            /**
190             * @return Returns the root.
191             */
192            public Resource getRoot() {
193                    return root;
194            }
195            
196            /**
197             * @return returns a variable string based on realpath and return it
198             */
199            public String getRealPathAsVariableString() {
200                    return StringUtil.toIdentityVariableName(getRealpath());
201                    //return StringUtil.toClassName(getRealpath());
202            }
203            
204            /**
205             * @return returns the a classname matching to filename
206             */
207            public String getClassName() {
208                    if(className==null) createClassAndPackage();
209                    return className;
210            }
211            
212            /**
213             * @return returns the java name
214             */
215            public String getJavaName() {
216                    if(javaName==null) createClassAndPackage();
217                    return javaName;
218            }
219            
220            /**
221             * @return returns the a package matching to file
222             */
223            public String getPackageName() {
224                    if(packageName==null) createClassAndPackage();
225                    return packageName;
226            }
227            
228            private void createClassAndPackage() {
229                    String str=realpath;
230                    StringBuilder packageName=new StringBuilder();
231                    StringBuilder javaName=new StringBuilder();
232                    while(str.indexOf('/')==0)str=str.substring(1);
233                    while(str.lastIndexOf('/')==str.length()-1)str=str.substring(0,str.length()-1);
234                    
235                    //String[] arr=str.split("/");
236                    Array arr = ListUtil.listToArray(str, '/');
237                    int len=arr.size();
238                    String value;
239                    for(int i=1;i<=len;i++) {
240                            value=(String) arr.get(i,"");
241                            String varName=StringUtil.toVariableName(value);
242                            javaName.append("/"+varName);
243                            if(i==len) {
244                                    className=varName.toLowerCase();
245                            }
246                            else {
247                                    if(i!=0) packageName.append('.');
248                                    packageName.append(varName);
249                            }
250                    }
251                    this.packageName=packageName.toString().toLowerCase();
252                    this.javaName=javaName.toString().toLowerCase();
253            }
254            
255            
256    
257            /**
258             * @return returns a variable string based on root and return it
259             */
260            public String getRootPathAsVariableString() {
261                    return StringUtil.toIdentityVariableName(root.getAbsolutePath());
262            }
263            
264        /**
265         * @return has context a archive or not
266         */
267        public boolean hasArchive() {
268            return hasArchive;
269        }
270        
271        /**
272         * @return returns if is trusted or not
273         */
274        public boolean isTrusted() {
275            return trusted;
276        }
277    
278        @Override
279        public Resource getPhyscalFile() {
280            return getFile();
281        }
282    
283        @Override
284        public String getDisplayPath() {
285            return getFile().getAbsolutePath();
286        }
287    
288            @Override
289            public String getFullClassName() {
290                    String p=getPackageName();
291                    if(p.length()==0) return getClassName();
292                    return p.concat(".").concat(getClassName());
293            }
294    }