Source code: com/tripi/asp/AspFileFactory.java
1 /**
2 * ArrowHead ASP Server
3 * This is a source file for the ArrowHead ASP Server - an 100% Java
4 * VBScript interpreter and ASP server.
5 *
6 * For more information, see http://www.tripi.com/arrowhead
7 *
8 * Copyright (C) 2002 Terence Haddock
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25 package com.tripi.asp;
26
27 import com.tripi.asp.parse.FileFactory;
28 import com.tripi.asp.*;
29 import jregex.*;
30 import org.apache.log4j.Category;
31 import java.io.FileInputStream;
32 import java.io.InputStream;
33 import java.io.IOException;
34 import java.io.File;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.Map;
38 import java.net.URL;
39
40 /**
41 * This class is a factory to handle finding files on the local file
42 * system. It is used by the other test routines.
43 */
44 public class AspFileFactory implements FileFactory
45 {
46 /** Debugging class */
47 private static final Category DBG = Category.getInstance(AspFileFactory.class);
48
49 /** Asp Content */
50 AspContext context;
51
52 /** Map of files loaded using this file factory, used to determine if
53 any of the files have changed. */
54 Map loadedFiles = new HashMap();
55
56 /**
57 * Constructor, initialized with an AspContext.
58 * @param context AspContext to initiaze to
59 */
60 public AspFileFactory(AspContext context)
61 {
62 this.context = context;
63 }
64
65 /**
66 * Resolve a relative file's location based on the given file.
67 * @param baseFile base file to use.
68 * @param relFile relative file to use.
69 */
70 public String resolveFile(String baseFilename, String relFile,
71 boolean virtual) throws AspException
72 {
73 if (DBG.isDebugEnabled())
74 DBG.debug("Resolve file: " + relFile + " / Base: " + baseFilename +
75 " / Virtual: " + virtual);
76 String absFilename;
77 if (isSeparatorChar(relFile.charAt(0)))
78 {
79 if (DBG.isDebugEnabled()) DBG.debug("Virtual file");
80 /* Absolute filename */
81 if (virtual)
82 {
83 Server server = context.getAspServer();
84 absFilename = server.MapPath(relFile);
85 } else {
86 absFilename = relFile;
87 }
88 } else {
89 /* Relative filename */
90 File baseFile = new File(baseFilename);
91 String path;
92 String basePath = baseFile.getParent();
93 if (basePath == null) basePath = "";
94 absFilename = concatPath(basePath, relFile);
95 }
96 if (DBG.isDebugEnabled()) DBG.debug("Result: " + absFilename);
97 return absFilename;
98 }
99
100 private final static boolean isSeparatorChar(char ch)
101 {
102 if (ch == File.separatorChar || ch == '/') return true;
103 return false;
104 }
105
106 /**
107 * Internal utility function to concatenate two paths.
108 * @param absPath Absolute path to start with
109 * @param addPath Relative path to add
110 */
111 private static String concatPath(String absPath, String addPath)
112 throws AspException
113 {
114 if (DBG.isDebugEnabled()) {
115 DBG.debug("concatPath(" + absPath + "," + addPath + ")");
116 }
117 final Pattern pathSep = new Pattern("/|\\\\");
118 String absPathStrings[] = pathSep.tokenizer(absPath).split();
119 String addPathStrings[] = pathSep.tokenizer(addPath).split();
120 String newPathStrings[] = new String[absPathStrings.length +
121 addPathStrings.length];
122 int absPathPos = absPathStrings.length - 1;
123 int addPathPos = 0;
124 int newPathPos = 0;
125 for (int i = 0; i < absPathStrings.length; i++) {
126 newPathStrings[i] = absPathStrings[i];
127 newPathPos++;
128 }
129 if (DBG.isDebugEnabled()) DBG.debug("newPathPos: " + newPathPos);
130 for (int i = 0; i < addPathStrings.length; i++) {
131 if (addPathStrings[i].equalsIgnoreCase(".")) continue;
132 if (addPathStrings[i].equalsIgnoreCase(".."))
133 {
134 if (newPathPos == 0) throw new AspException("Invalid path: " + absPath + File.pathSeparator + addPath);
135 newPathPos--;
136 continue;
137 }
138 newPathStrings[newPathPos++] = addPathStrings[i];
139 }
140 if (DBG.isDebugEnabled()) DBG.debug("newPathPos: " + newPathPos);
141 StringBuffer buf = new StringBuffer();
142 if (isSeparatorChar(absPath.charAt(0)))
143 {
144 buf.append(File.separatorChar);
145 }
146 for (int i = 0; i < newPathPos; i++)
147 {
148 if (i != 0) buf.append(File.separatorChar);
149 buf.append(newPathStrings[i]);
150 }
151 if (DBG.isDebugEnabled()) DBG.debug("Final path: " + buf.toString());
152 return buf.toString();
153 }
154
155 /**
156 * Get the file resource.
157 * @param file file to obtain stream of
158 * @return input stream of the specified file
159 */
160 public InputStream getResource(String filename) throws AspException
161 {
162 try {
163 Server server = context.getAspServer();
164 FileInformation finfo = getFileInformation(filename);
165 return new FileInputStream(finfo.file);
166 } catch (IOException ex) {
167 throw new AspNestedException(ex);
168 }
169 }
170
171 /**
172 * Clear the loaded files cache.
173 */
174 public void clearLoadedFilesCache()
175 {
176 loadedFiles = new HashMap();
177 }
178
179 /**
180 * Check if any of the cached files have been modified on the disk.
181 * @return <b>true</b> if a file has been modified, <b>false</b> otherwise.
182 */
183 public boolean isModified()
184 {
185 synchronized(loadedFiles)
186 {
187 for (Iterator i = loadedFiles.values().iterator(); i.hasNext();)
188 {
189 FileInformation finfo = (FileInformation)i.next();
190 if (DBG.isDebugEnabled())
191 {
192 DBG.debug("finfo.file.LastModified() = " +
193 finfo.file.lastModified());
194 DBG.debug("finfo.lastModifiedTime = " +
195 finfo.lastModifiedTime);
196 }
197 if (finfo.file.lastModified() > finfo.lastModifiedTime)
198 {
199 return true;
200 }
201 }
202 }
203 return false;
204 }
205
206 /**
207 * Get the cached file information for the file.
208 */
209 private FileInformation getFileInformation(String fileLocation)
210 {
211 synchronized(loadedFiles)
212 {
213 if (!loadedFiles.containsKey(fileLocation))
214 {
215 FileInformation finfo = new FileInformation();
216 finfo.file = new File(fileLocation);
217 finfo.lastModifiedTime = finfo.file.lastModified();
218 loadedFiles.put(fileLocation, finfo);
219 }
220 return (FileInformation)loadedFiles.get(fileLocation);
221 }
222 }
223
224 /**
225 * Class to contain cached file information.
226 */
227 private static class FileInformation
228 {
229 /** File handle */
230 File file;
231
232 /** Last modified time */
233 long lastModifiedTime;
234 }
235 }