View Javadoc
1   /*
2    * Copyright (C) 2002-2003,2017-2023 Dipl.-Inform. Kai Hofmann. All rights reserved!
3    */
4   package de.powerstat.phplib.templateengine.intern;
5   
6   
7   import java.io.BufferedReader;
8   import java.io.File;
9   import java.io.FileNotFoundException;
10  import java.io.IOException;
11  import java.io.InputStream;
12  import java.io.InputStreamReader;
13  import java.nio.charset.StandardCharsets;
14  import java.nio.file.Files;
15  import java.nio.file.StandardOpenOption;
16  import java.util.Map;
17  import java.util.Objects;
18  import java.util.concurrent.ConcurrentHashMap;
19  
20  import org.apache.logging.log4j.LogManager;
21  import org.apache.logging.log4j.Logger;
22  
23  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
24  
25  
26  /**
27   * Template file manager.
28   */
29  public final class FileManager
30   {
31    /**
32     * Logger.
33     */
34    private static final Logger LOGGER = LogManager.getLogger(FileManager.class);
35  
36    /**
37     * Maximum template size.
38     */
39    private static final int MAX_TEMPLATE_SIZE = 1048576;
40  
41    /**
42     * File path separator.
43     */
44    private static final String FILEPATH_SEPARATOR = "/"; //$NON-NLS-1$
45  
46    /**
47     * Variable manager reference.
48     */
49    private final VariableManager variableManager;
50  
51    /**
52     * File name map.
53     */
54    private final Map<String, File> files = new ConcurrentHashMap<>();
55  
56  
57    /**
58     * Copy constructor.
59     *
60     * @param vManager Variable manager
61     * @param fManager File manager to copy from
62     * @throws NullPointerException If vManager or fManager is null
63     */
64    @SuppressFBWarnings("EI_EXPOSE_REP2")
65    public FileManager(final VariableManager vManager, final FileManager fManager)
66     {
67      super();
68      Objects.requireNonNull(vManager, "vManager"); //$NON-NLS-1$
69      Objects.requireNonNull(fManager, "fManager"); //$NON-NLS-1$
70      this.variableManager = vManager;
71      for (final Map.Entry<String, File> entry : fManager.files.entrySet())
72       {
73        this.files.put(entry.getKey(), entry.getValue());
74       }
75     }
76  
77  
78    /**
79     * Constructor.
80     *
81     * @param vManager Variable manager
82     */
83    @SuppressFBWarnings("EI_EXPOSE_REP2")
84    public FileManager(final VariableManager vManager)
85     {
86      super();
87      this.variableManager = vManager;
88     }
89  
90  
91    /**
92     * Add template file for variable.
93     *
94     * @param newVarname Variable that should hold the template
95     * @param newFile Template file UTF-8 encoded
96     * @return true when successful (file exists) otherwise false
97     * @throws NullPointerException If newVarname or newFile is null
98     * @throws IllegalArgumentException If newVarname is empty
99     */
100   @SuppressWarnings({"PMD.LinguisticNaming", "java:S3457"})
101   public boolean addFile(final String newVarname, final File newFile)
102    {
103     // asserts
104     boolean exists = newFile.exists();
105     if (exists)
106      {
107       if (newFile.length() > FileManager.MAX_TEMPLATE_SIZE)
108        {
109         throw new IllegalArgumentException("newFile to large"); //$NON-NLS-1$
110        }
111       this.files.put(newVarname, newFile);
112      }
113     else
114      {
115       try (InputStream stream = this.getClass().getResourceAsStream(FileManager.FILEPATH_SEPARATOR + newFile.getName()))
116        {
117         if (stream != null)
118          {
119           exists = true;
120           this.files.put(newVarname, newFile);
121          }
122        }
123       catch (final IOException ignored)
124        {
125         // exists is already false
126         FileManager.LOGGER.warn("File does not exist: " + newFile.getAbsolutePath(), ignored); //$NON-NLS-1$
127        }
128      }
129     return exists;
130    }
131 
132 
133   /**
134    * Exists file for varname.
135    *
136    * @param varname Variable to read from file
137    * @return true if file exists, false otherwise
138    */
139   public boolean existsFile(final String varname)
140    {
141     final var file = this.files.get(varname);
142     return (file != null);
143    }
144 
145 
146   /**
147    * Load template file (UTF-8 encoded) if required.
148    *
149    * @param varname Variable to read from file
150    * @return true if successful otherwise false
151    * @throws FileNotFoundException File not found
152    * @throws IOException IO exception
153    */
154   @SuppressWarnings("PMD.CloseResource")
155   public boolean loadFile(final String varname) throws IOException
156    {
157     // assert (varname != null) && !varname.isEmpty() && (varname.length() <= TemplateEngine.MAX_VARNAME_SIZE);
158     if (this.variableManager.existsVar(varname)) // Already loaded?
159      {
160       return true;
161      }
162     final var file = this.files.get(varname);
163     if (file == null)
164      {
165       return false;
166      }
167     InputStream istream = this.getClass().getResourceAsStream(FileManager.FILEPATH_SEPARATOR + file.getName()); // Read from classpath/jar
168     if (istream == null)
169      {
170       istream = Files.newInputStream(this.files.get(varname).toPath(), StandardOpenOption.READ); // Read from filesystem
171      }
172     final var fileBuffer = new StringBuilder();
173     try (var reader = new BufferedReader(new InputStreamReader(istream, StandardCharsets.UTF_8)))
174      {
175       String line = reader.readLine();
176       while (line != null)
177        {
178         fileBuffer.append(line);
179         fileBuffer.append('\n');
180         line = reader.readLine();
181        }
182      }
183     if (fileBuffer.length() == 0)
184      {
185       return false;
186      }
187     this.variableManager.setVar(varname, fileBuffer.toString());
188     return true;
189    }
190 
191 
192   /**
193    * Returns the string representation of this FileManager.
194    *
195    * The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
196    *
197    * "FileManager[files=[name, ...]]"
198    *
199    * @return String representation of this FileManager.
200    * @see java.lang.Object#toString()
201    */
202   @Override
203   public String toString()
204    {
205     return new StringBuilder().append("FileManager[").append("files=").append(this.files.values().stream().map(File::getName).reduce((s1, s2) -> s1 + ", " + s2)).append(']').toString(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
206    }
207 
208 
209   /**
210    * Calculate hash code.
211    *
212    * @return Hash
213    * @see java.lang.Object#hashCode()
214    */
215   @Override
216   public int hashCode()
217    {
218     return Objects.hash(this.files);
219    }
220 
221 
222   /**
223    * Is equal with another object.
224    *
225    * @param obj Object
226    * @return true when equal, false otherwise
227    * @see java.lang.Object#equals(java.lang.Object)
228    */
229   @Override
230   public boolean equals(final Object obj)
231    {
232     if (this == obj)
233      {
234       return true;
235      }
236     if (!(obj instanceof FileManager))
237      {
238       return false;
239      }
240     final FileManager other = (FileManager)obj;
241     return this.files.equals(other.files) && this.variableManager.equals(other.variableManager);
242    }
243 
244  }