001package gu.sql2java.generator; 002 003import java.io.BufferedInputStream; 004import java.io.ByteArrayInputStream; 005import java.io.File; 006import java.io.FileInputStream; 007import java.io.FileNotFoundException; 008import java.io.IOException; 009import java.io.InputStream; 010import java.util.ArrayList; 011import java.util.Collections; 012import java.util.HashMap; 013import java.util.List; 014import java.util.Map; 015import java.util.Vector; 016 017import org.apache.commons.collections.ExtendedProperties; 018import org.apache.velocity.exception.ResourceNotFoundException; 019import org.apache.velocity.exception.VelocityException; 020import org.apache.velocity.io.UnicodeInputStream; 021import org.apache.velocity.runtime.resource.Resource; 022import org.apache.velocity.runtime.resource.loader.ResourceLoader; 023import org.apache.velocity.util.StringUtils; 024 025import com.google.common.base.Joiner; 026 027/** 028 * 文件资源加载器<br> 029 * 基于 {@link org.apache.velocity.runtime.resource.loader.FileResourceLoader}修改.<br> 030 * 当指定资源名为文件夹时输出文件夹中的文件名列表('\n'分隔) 031 * 032 * @author guyadong 033 * 034 */ 035public class Sql2javaFileResourceLoader extends ResourceLoader { 036 /** 037 * The paths to search for templates. 038 */ 039 private List<String> paths = new ArrayList<>(); 040 041 /** 042 * Used to map the path that a template was found on 043 * so that we can properly check the modification 044 * times of the files. This is synchronizedMap 045 * instance. 046 */ 047 private Map<String, String> templatePaths = Collections.synchronizedMap(new HashMap<String,String>()); 048 049 /** Shall we inspect unicode files to see what encoding they contain?. */ 050 private boolean unicode = false; 051 052 /** 053 * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(org.apache.commons.collections.ExtendedProperties) 054 */ 055 @SuppressWarnings("unchecked") 056 public void init( ExtendedProperties configuration) 057 { 058 if (log.isTraceEnabled()) 059 { 060 log.trace("Sql2javaFileResourceLoader : initialization starting."); 061 } 062 063 paths.addAll( (Vector<String>)configuration.getVector("path") ); 064 065 // unicode files may have a BOM marker at the start, but Java 066 // has problems recognizing the UTF-8 bom. Enabling unicode will 067 // recognize all unicode boms. 068 unicode = configuration.getBoolean("unicode", false); 069 070 if (log.isDebugEnabled()) 071 { 072 log.debug("Do unicode file recognition: " + unicode); 073 } 074 075 if (log.isDebugEnabled()) 076 { 077 // trim spaces from all paths 078 StringUtils.trimStrings(paths); 079 080 // this section lets tell people what paths we will be using 081 int sz = paths.size(); 082 for( int i=0; i < sz; i++) 083 { 084 log.debug("Sql2javaFileResourceLoader : adding path '" + (String) paths.get(i) + "'"); 085 } 086 log.trace("Sql2javaFileResourceLoader : initialization complete."); 087 } 088 } 089 090 /** 091 * Get an InputStream so that the Runtime can build a 092 * template with it. 093 * 094 * @param templateName name of template to get 095 * @return InputStream containing the template 096 * @throws ResourceNotFoundException if template not found 097 * in the file template path. 098 */ 099 public InputStream getResourceStream(String templateName) 100 throws ResourceNotFoundException 101 { 102 /* 103 * Make sure we have a valid templateName. 104 */ 105 if (org.apache.commons.lang.StringUtils.isEmpty(templateName)) 106 { 107 /* 108 * If we don't get a properly formed templateName then 109 * there's not much we can do. So we'll forget about 110 * trying to search any more paths for the template. 111 */ 112 throw new ResourceNotFoundException( 113 "Need to specify a file name or file path!"); 114 } 115 try { 116 InputStream inputStream = findTemplate("",templateName); 117 if(inputStream != null){ 118 templatePaths.put(templateName, ""); 119 return inputStream; 120 } 121 } 122 catch (IOException ioe) 123 { 124 String msg = "Exception while loading Resource " + templateName; 125 log.error(msg, ioe); 126 throw new VelocityException(msg, ioe); 127 } 128 String template = templateName; 129 130 int size = paths.size(); 131 for (int i = 0; i < size; i++) 132 { 133 String path =paths.get(i); 134 InputStream inputStream = null; 135 136 try 137 { 138 inputStream = findTemplate(path, template); 139 } 140 catch (IOException ioe) 141 { 142 String msg = "Exception while loading Resource " + template; 143 log.error(msg, ioe); 144 throw new VelocityException(msg, ioe); 145 } 146 147 if (inputStream != null) 148 { 149 /* 150 * Store the path that this template came 151 * from so that we can check its modification 152 * time. 153 */ 154 templatePaths.put(templateName, path); 155 return inputStream; 156 } 157 } 158 159 /* 160 * We have now searched all the paths for 161 * templates and we didn't find anything so 162 * throw an exception. 163 */ 164 throw new ResourceNotFoundException("Sql2javaFileResourceLoader : cannot find " + template); 165 } 166 167 /** 168 * Overrides superclass for better performance. 169 * @since 1.6 170 */ 171 public boolean resourceExists(String name) 172 { 173 if (name == null) 174 { 175 return false; 176 } 177 name = StringUtils.normalizePath(name); 178 if (name == null || name.length() == 0) 179 { 180 return false; 181 } 182 183 int size = paths.size(); 184 for (int i = 0; i < size; i++) 185 { 186 String path = (String)paths.get(i); 187 try 188 { 189 File file = getFile(path, name); 190 if (file.canRead()) 191 { 192 return true; 193 } 194 } 195 catch (Exception ioe) 196 { 197 String msg = "Exception while checking for template " + name; 198 log.debug(msg, ioe); 199 } 200 } 201 return false; 202 } 203 204 /** 205 * Try to find a template given a normalized path. 206 * 207 * @param path a normalized path 208 * @param template name of template to find 209 * @return InputStream input stream that will be parsed 210 * 211 */ 212 private InputStream findTemplate(final String path, final String template) 213 throws IOException 214 { 215 try 216 { 217 File file = getFile(path,template); 218 InputStream is = null; 219 if (file.isFile()) 220 { 221 is = new FileInputStream(file.getAbsolutePath()); 222 } 223 else if (file.isDirectory()) 224 { 225 // 资源路径为文件夹时输出文件夹中文件名列表('\n'分隔) 226 String[] dirEntires = file.list(); 227 // 文件夹名后加'/'区分 228 for(int i=0; i<dirEntires.length; ++i){ 229 if(new File(file,dirEntires[i]).isDirectory()){ 230 dirEntires[i] += "/"; 231 } 232 } 233 is = new ByteArrayInputStream(Joiner.on('\n').join(dirEntires).getBytes()); 234 } 235 else 236 { 237 return null; 238 } 239 if (unicode) 240 { 241 UnicodeInputStream uis = null; 242 243 try 244 { 245 uis = new UnicodeInputStream(is, true); 246 247 if (log.isDebugEnabled()) 248 { 249 log.debug("File Encoding for " + file + " is: " + uis.getEncodingFromStream()); 250 } 251 252 return new BufferedInputStream(uis); 253 } 254 catch(IOException e) 255 { 256 closeQuiet(uis); 257 throw e; 258 } 259 } 260 else 261 { 262 return new BufferedInputStream(is); 263 } 264 } 265 catch(FileNotFoundException fnfe) 266 { 267 /* 268 * log and convert to a general Velocity ResourceNotFoundException 269 */ 270 return null; 271 } 272 } 273 274 private void closeQuiet(final InputStream is) 275 { 276 if (is != null) 277 { 278 try 279 { 280 is.close(); 281 } 282 catch(IOException ioe) 283 { 284 // Ignore 285 } 286 } 287 } 288 289 /** 290 * How to keep track of all the modified times 291 * across the paths. Note that a file might have 292 * appeared in a directory which is earlier in the 293 * path; so we should search the path and see if 294 * the file we find that way is the same as the one 295 * that we have cached. 296 * @param resource 297 * @return True if the source has been modified. 298 */ 299 public boolean isSourceModified(Resource resource) 300 { 301 /* 302 * we assume that the file needs to be reloaded; 303 * if we find the original file and it's unchanged, 304 * then we'll flip this. 305 */ 306 boolean modified = true; 307 308 String fileName = resource.getName(); 309 String path = (String) templatePaths.get(fileName); 310 File currentFile = null; 311 312 for (int i = 0; currentFile == null && i < paths.size(); i++) 313 { 314 String testPath = (String) paths.get(i); 315 File testFile = getFile(testPath, fileName); 316 if (testFile.canRead()) 317 { 318 currentFile = testFile; 319 } 320 } 321 File file = getFile(path, fileName); 322 if (currentFile == null || !file.exists()) 323 { 324 /* 325 * noop: if the file is missing now (either the cached 326 * file is gone, or the file can no longer be found) 327 * then we leave modified alone (it's set to true); a 328 * reload attempt will be done, which will either use 329 * a new template or fail with an appropriate message 330 * about how the file couldn't be found. 331 */ 332 } 333 else if (currentFile.equals(file) && file.canRead()) 334 { 335 /* 336 * if only if currentFile is the same as file and 337 * file.lastModified() is the same as 338 * resource.getLastModified(), then we should use the 339 * cached version. 340 */ 341 modified = (file.lastModified() != resource.getLastModified()); 342 } 343 344 /* 345 * rsvc.debug("isSourceModified for " + fileName + ": " + modified); 346 */ 347 return modified; 348 } 349 350 /** 351 * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#getLastModified(org.apache.velocity.runtime.resource.Resource) 352 */ 353 public long getLastModified(Resource resource) 354 { 355 String path = (String) templatePaths.get(resource.getName()); 356 File file = getFile(path, resource.getName()); 357 358 if (file.canRead()) 359 { 360 return file.lastModified(); 361 } 362 else 363 { 364 return 0; 365 } 366 } 367 368 369 /** 370 * Create a File based on either a relative path if given, or absolute path otherwise 371 */ 372 private File getFile(String path, String template) 373 { 374 375 File file = null; 376 377 if("".equals(path)) 378 { 379 file = new File( template ); 380 } 381 else 382 { 383 /* 384 * if a / leads off, then just nip that :) 385 */ 386 if (template.startsWith("/")) 387 { 388 template = template.substring(1); 389 } 390 391 file = new File ( path, template ); 392 } 393 394 return file; 395 } 396}