ResourceLoaderAdaptor.java revision 56ed4167b942ec265f9cee70ac4d71d10b3835ce
19648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov/* 29648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * Copyright (C) 2010 Google Inc. 39648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * 49648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * Licensed under the Apache License, Version 2.0 (the "License"); 59648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * you may not use this file except in compliance with the License. 69648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * You may obtain a copy of the License at 79648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * 89648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * http://www.apache.org/licenses/LICENSE-2.0 99648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * 109648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * Unless required by applicable law or agreed to in writing, software 119648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * distributed under the License is distributed on an "AS IS" BASIS, 129648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * See the License for the specific language governing permissions and 149648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * limitations under the License. 159648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov */ 169648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 179648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovpackage com.google.clearsilver.jsilver.adaptor; 189648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 199648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport com.google.clearsilver.jsilver.exceptions.JSilverTemplateNotFoundException; 209648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport com.google.clearsilver.jsilver.resourceloader.ResourceLoader; 219648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 229648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport org.clearsilver.CSFileLoader; 239648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport org.clearsilver.CSUtil; 249648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 259648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport java.io.File; 269648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport java.io.FileInputStream; 279648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport java.io.FileNotFoundException; 289648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport java.io.IOException; 299648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport java.io.InputStreamReader; 309648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport java.io.Reader; 319648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport java.io.StringReader; 329648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovimport java.util.List; 339648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 349648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov/** 359648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * Wrap a CSFileLoader with a ResourceLoader 369648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov */ 379648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganovpublic class ResourceLoaderAdaptor implements ResourceLoader { 389648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 399648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov private final JHdf hdf; 409648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov private final LoadPathToFileCache loadPathCache; 419648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov private final CSFileLoader csFileLoader; 429648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov private List<String> loadPaths; 439648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 449648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov ResourceLoaderAdaptor(JHdf hdf, LoadPathToFileCache loadPathCache, CSFileLoader csFileLoader) { 459648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov this.hdf = hdf; 469648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov this.loadPathCache = loadPathCache; 479648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov this.csFileLoader = csFileLoader; 489648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 499648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 509648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov @Override 519648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov public Reader open(String name) throws IOException { 529648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov if (csFileLoader != null) { 539648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov if (hdf.getData() == null) { 549648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov throw new IllegalStateException("HDF is already closed"); 559648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 569648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov return new StringReader(csFileLoader.load(hdf, name)); 579648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } else { 589648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov File file = locateFile(name); 599648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov if (file == null) { 609648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov throw new FileNotFoundException("Could not locate file " + name); 619648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 629648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov return new InputStreamReader(new FileInputStream(file), "UTF-8"); 639648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 649648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 659648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 669648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov @Override 679648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov public Reader openOrFail(String name) throws JSilverTemplateNotFoundException, IOException { 689648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov Reader reader = open(name); 699648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov if (reader == null) { 709648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov final StringBuffer text = new StringBuffer(); 719648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append("No file '"); 729648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append(name); 739648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append("' "); 749648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov if (loadPaths == null || loadPaths.isEmpty()) { 759648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append("with no load paths"); 769648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } else if (loadPaths.size() == 1) { 779648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append("inside directory '"); 789648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append(loadPaths.get(0)); 799648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append("'"); 809648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } else { 819648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append("inside directories ( "); 829648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov for (String path : getLoadPaths()) { 839648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append("'"); 849648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append(path); 859648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append("' "); 869648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 879648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov text.append(")"); 889648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 899648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov throw new JSilverTemplateNotFoundException(text.toString()); 909648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } else { 919648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov return reader; 929648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 939648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 949648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 959648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov /** 969648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * 979648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * @param name name of the file to locate. 989648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov * @return a File object corresponding to the existing file or {@code null} if it does not exist. 999648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov */ 1009648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov File locateFile(String name) { 1019648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov if (name.startsWith(File.separator)) { 1029648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov // Full path to file was given. 1039648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov File file = newFile(name); 1049648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov return file.exists() ? file : null; 1059648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 1069648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov File file = null; 1079648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov // loadPathCache is null when load path caching is disabled at the 1089648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov // JSilverFactory level. This is implied by setting cache size 1099648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov // to 0 using JSilverOptions.setLoadPathCacheSize(0). 1109648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov if (loadPathCache != null) { 1119648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov String filePath = loadPathCache.lookup(getLoadPaths(), name); 1129648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov if (filePath != null) { 1139648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov file = newFile(filePath); 1149648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov return file.exists() ? file : null; 1159648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 1169648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov } 1179648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 1189648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov file = locateFile(getLoadPaths(), name); 1199648c538bac4f04145c118cc41168d1d7a536312Svetoslav Ganov 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 */ 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 */ 156 File newFile(String filename) { 157 return new File(filename); 158 } 159 160 File newFile(String path, String filename) { 161 return new File(path, filename); 162 } 163 164 @Override 165 public void close(Reader reader) throws IOException { 166 reader.close(); 167 } 168 169 @Override 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 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 205 final CSFileLoader getCSFileLoader() { 206 return csFileLoader; 207 } 208 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