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}