1 /* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.clearsilver.jsilver.adaptor; 18 19 import com.google.clearsilver.jsilver.exceptions.JSilverTemplateNotFoundException; 20 import com.google.clearsilver.jsilver.resourceloader.ResourceLoader; 21 22 import org.clearsilver.CSFileLoader; 23 import org.clearsilver.CSUtil; 24 25 import java.io.File; 26 import java.io.FileInputStream; 27 import java.io.FileNotFoundException; 28 import java.io.IOException; 29 import java.io.InputStreamReader; 30 import java.io.Reader; 31 import java.io.StringReader; 32 import java.util.List; 33 34 /** 35 * Wrap a CSFileLoader with a ResourceLoader 36 */ 37 public class ResourceLoaderAdaptor implements ResourceLoader { 38 39 private final JHdf hdf; 40 private final LoadPathToFileCache loadPathCache; 41 private final CSFileLoader csFileLoader; 42 private List<String> loadPaths; 43 ResourceLoaderAdaptor(JHdf hdf, LoadPathToFileCache loadPathCache, CSFileLoader csFileLoader)44 ResourceLoaderAdaptor(JHdf hdf, LoadPathToFileCache loadPathCache, CSFileLoader csFileLoader) { 45 this.hdf = hdf; 46 this.loadPathCache = loadPathCache; 47 this.csFileLoader = csFileLoader; 48 } 49 50 @Override open(String name)51 public Reader open(String name) throws IOException { 52 if (csFileLoader != null) { 53 if (hdf.getData() == null) { 54 throw new IllegalStateException("HDF is already closed"); 55 } 56 return new StringReader(csFileLoader.load(hdf, name)); 57 } else { 58 File file = locateFile(name); 59 if (file == null) { 60 throw new FileNotFoundException("Could not locate file " + name); 61 } 62 return new InputStreamReader(new FileInputStream(file), "UTF-8"); 63 } 64 } 65 66 @Override openOrFail(String name)67 public Reader openOrFail(String name) throws JSilverTemplateNotFoundException, IOException { 68 Reader reader = open(name); 69 if (reader == null) { 70 final StringBuffer text = new StringBuffer(); 71 text.append("No file '"); 72 text.append(name); 73 text.append("' "); 74 if (loadPaths == null || loadPaths.isEmpty()) { 75 text.append("with no load paths"); 76 } else if (loadPaths.size() == 1) { 77 text.append("inside directory '"); 78 text.append(loadPaths.get(0)); 79 text.append("'"); 80 } else { 81 text.append("inside directories ( "); 82 for (String path : getLoadPaths()) { 83 text.append("'"); 84 text.append(path); 85 text.append("' "); 86 } 87 text.append(")"); 88 } 89 throw new JSilverTemplateNotFoundException(text.toString()); 90 } else { 91 return reader; 92 } 93 } 94 95 /** 96 * 97 * @param name name of the file to locate. 98 * @return a File object corresponding to the existing file or {@code null} if it does not exist. 99 */ locateFile(String name)100 File locateFile(String name) { 101 if (name.startsWith(File.separator)) { 102 // Full path to file was given. 103 File file = newFile(name); 104 return file.exists() ? file : null; 105 } 106 File file = null; 107 // loadPathCache is null when load path caching is disabled at the 108 // JSilverFactory level. This is implied by setting cache size 109 // to 0 using JSilverOptions.setLoadPathCacheSize(0). 110 if (loadPathCache != null) { 111 String filePath = loadPathCache.lookup(getLoadPaths(), name); 112 if (filePath != null) { 113 file = newFile(filePath); 114 return file.exists() ? file : null; 115 } 116 } 117 118 file = locateFile(getLoadPaths(), name); 119 if (file != null && loadPathCache != null) { 120 loadPathCache.add(getLoadPaths(), name, file.getAbsolutePath()); 121 } 122 return file; 123 } 124 125 /** 126 * Given an ordered list of directories to look in, locate the specified file. Returns 127 * <code>null</code> if file not found. 128 * <p> 129 * This is copied from {@link org.clearsilver.CSUtil#locateFile(java.util.List, String)} but has 130 * one important difference. It calls our subclassable newFile method. 131 * 132 * @param loadPaths the ordered list of paths to search. 133 * @param filename the name of the file. 134 * @return a File object corresponding to the file. <code>null</code> if file not found. 135 */ locateFile(List<String> loadPaths, String filename)136 File locateFile(List<String> loadPaths, String filename) { 137 if (filename == null) { 138 throw new NullPointerException("No filename provided"); 139 } 140 if (loadPaths == null) { 141 throw new NullPointerException("No loadpaths provided."); 142 } 143 for (String path : loadPaths) { 144 File file = newFile(path, filename); 145 if (file.exists()) { 146 return file; 147 } 148 } 149 return null; 150 } 151 152 /** 153 * Separate methods to allow tests to subclass and override File creation and return mocks or 154 * fakes. 155 */ newFile(String filename)156 File newFile(String filename) { 157 return new File(filename); 158 } 159 newFile(String path, String filename)160 File newFile(String path, String filename) { 161 return new File(path, filename); 162 } 163 164 @Override close(Reader reader)165 public void close(Reader reader) throws IOException { 166 reader.close(); 167 } 168 169 @Override getKey(String filename)170 public Object getKey(String filename) { 171 if (filename.startsWith(File.separator)) { 172 return filename; 173 } else { 174 File file = locateFile(filename); 175 if (file == null) { 176 // The file does not exist, use the full loadpath and file name as the 177 // key. 178 return LoadPathToFileCache.makeCacheKey(getLoadPaths(), filename); 179 } else { 180 return file.getAbsolutePath(); 181 } 182 } 183 } 184 185 /** 186 * Some applications, e.g. online help, need to know when a file has changed due to a symlink 187 * modification hence the use of {@link File#getCanonicalFile()}, if possible. 188 */ 189 @Override getResourceVersionId(String filename)190 public Object getResourceVersionId(String filename) { 191 File file = locateFile(filename); 192 if (file == null) { 193 return null; 194 } 195 196 String fullPath; 197 try { 198 fullPath = file.getCanonicalPath(); 199 } catch (IOException e) { 200 fullPath = file.getAbsolutePath(); 201 } 202 return String.format("%s@%s", fullPath, file.lastModified()); 203 } 204 getCSFileLoader()205 final CSFileLoader getCSFileLoader() { 206 return csFileLoader; 207 } 208 getLoadPaths()209 private synchronized List<String> getLoadPaths() { 210 if (loadPaths == null) { 211 if (hdf.getData() == null) { 212 throw new IllegalStateException("HDF is already closed"); 213 } 214 loadPaths = CSUtil.getLoadPaths(hdf, true); 215 } 216 return loadPaths; 217 } 218 } 219