Home | History | Annotate | Download | only in adaptor
      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 
     44   ResourceLoaderAdaptor(JHdf hdf, LoadPathToFileCache loadPathCache, CSFileLoader csFileLoader) {
     45     this.hdf = hdf;
     46     this.loadPathCache = loadPathCache;
     47     this.csFileLoader = csFileLoader;
     48   }
     49 
     50   @Override
     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
     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    */
    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    */
    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