View Javadoc
1   
2   package eu.simuline.m2latex.core;
3   
4   import java.io.BufferedReader;
5   import java.io.File;
6   import java.io.FileReader;
7   import java.io.IOException;
8   
9   import com.florianingerl.util.regex.Matcher;
10  import com.florianingerl.util.regex.Pattern;
11  
12  
13  /**
14   * Very bad name: 
15   * Represents some aspect of postprocessing 
16   * performed in {@link LatexProcessor#processLatex2devCore(LatexMainDesc, LatexDev)}. 
17   * No it is auxiliary processing, 
18   * because it is alternating auxiliary tools and latex compiler. 
19   * <p>
20   * Auxiliary processing consists of two parts: 
21   * <ul>
22   * <li>the run of a latex compiler, typically via a package loaded, 
23   * writes (into) a file. 
24   * This may be a specific file or a file common for more auxiliary processings. 
25   * At time of this writing this is the AUX file. 
26   * In that case, the latex run writes lines into the file 
27   * which are specific for the auxiliary processing. 
28   * <li>the subsequent run of an auxiliary program, 
29   * triggered if that specific file is present, 
30   * or if the specific lines in a common file are present. 
31   * The auxiliary program reads the lines of that file 
32   * and writes some file, typically another one, 
33   * which is read by the next run of the latex compiler. 
34   * </ul>
35   * 
36   * As described above, the initial run of the latex compiler 
37   * writes (lines into) a file, 
38   * the presence of which triggers invocation of the corresponding auxiliary program 
39   * which writes some file which is read by the next run of the latex compiler. 
40   * If the compiler writes a different file, 
41   * of course the auxiliary program must be rerun and so on. 
42   * There are really cases where this does not terminate 
43   * so to ensure termination a maximal number of runs of the latex compiler is specified. 
44   * <p>
45   * Creating a bibliography with <code>bibtex</code> or equivalent, 
46   * is one of the auxiliary processings {@link #BibTex}. 
47   * It is a bit special in that the latex compiler does not need a special package 
48   * to write the bibliographical information into the AUX file. 
49   * The auxiliary program <code>bibtex</code> or that like extracts and sorts the entries 
50   * into a BBL file which is read back by the latex compiler 
51   * to create the bibliography. 
52   * CAUTION: the lines of the AUX file read by <code>bibtex</code> 
53   * may be altered by subsequent calls of a latex compiler or of an auxiliary program 
54   * as shown by https://tex.stackexchange.com/questions/724138/is-backreference-possible-with-bibtex-maybe-in-conjunction-with-biblatex. 
55   * <p>
56   * At time of this writing, bibliography with package <code>biblatex</code> 
57   * in conjunction with auxiliary program <code>biber</code> is not yet supported. 
58   * <p>
59   * Writing an index is only supported via package <code>idxindex</code> 
60   * and auxiliary program <code>makeindex</code>. 
61   * This includes <code>splitindex</code> for multiple indices 
62   * invoking <code>makeindex</code> internally. 
63   * This is {@link #Idx}. 
64   * Clearly a subsequent run of the latex compiler changes the index 
65   * if the index entry moves to another page number. 
66   * <p>
67   * At time of this writing, indices with package <code>???</code> 
68   * in conjunction with auxiliary program <code>xindy</code> is not yet supported. 
69   * 
70   */
71  enum Auxiliary {
72  
73    /**
74     * Bibliography processing with bibtex or related. 
75     * TBC: in conjunction with biblatex package also? 
76     * Maybe this shall not be included, because no rerun needed. 
77     * Then the process we want to introduce is superluous. 
78     */
79    BibTex {
80      // CAUTION: it is not .bib, it is .aux. 
81      // TBD: it is only part of .aux and also: 
82      // for includes the files referenced therein are also to be taken into account. 
83      String extension() {
84        return ".aux";
85      }
86  
87      boolean doesFitAuxiliary(File file) {
88        return doesFitAuxiliary(file, PATTERN_NEED_BIBTEX_RUN);
89      }
90  
91  
92      int numRunsAfter() {
93        return 2;
94      }
95  
96      /**
97       * Takes not only the AUX file <code>file</code> 
98       * into account, but also files included 
99       * via command {@link #PATTERN_INPUT}. 
100      */
101     FileId getIdent(File file) throws IOException {
102       //System.out.println("update:Bibtex");
103       return updateIdentInclude(file, new FileId(), PATTERN_BIBTEX);
104     }
105 
106     boolean process(LatexMainDesc desc, LatexProcessor proc)
107         throws BuildFailureException {
108       return proc.runBibtex(desc);
109     }
110 
111 
112 
113 
114   },
115   // /**
116   //  * Bibliography processing with biblatex and biber. 
117   //  * TBC: is this really the sole case with backrefence 
118   //  * which may force rerun? 
119   //  */
120   // BibEr {
121   //   // CAUTION: it is not .bib, it is 
122   //   String extension() {
123   //     return ".bcf";
124   //   }
125   //},
126   // TBD: index with xindy 
127 
128   /**
129    * Index processing with makeindex and possibly splitindex. 
130    */
131   Idx {
132     String extension() {
133       return ".idx";
134     }
135 
136     boolean process(LatexMainDesc desc, LatexProcessor proc)
137         throws BuildFailureException {
138       return proc.runMakeSplitIndex(desc);
139     }
140   },
141   Glo {
142     String extension() {
143       return ".aux";
144     }
145 
146     boolean doesFitAuxiliary(File file) {
147       return doesFitAuxiliary(file, PATTERN_NEED_MAKEGLOSSARIES_RUN);
148     }
149 
150     FileId getIdent(File file) throws IOException {
151       //System.out.println("update:Bibtex");
152       return updateIdentGls(file, new FileId());
153     }
154 
155     // This need not be a seprate function. 
156     // It is, to be more extensible 
157     // if relevant material turns out to occur 
158     // which is not in the top level AUX file. 
159     /**
160      * Updates the identifier <code>fileId</code> 
161      * taking lines in <code>file</code> into account 
162      * matching {@link #PATTERN_INPUT_GLOSSARY} 
163      * indicating the presence of a specific glossary 
164      * and taking whole files into account 
165      * the name of which is the name of this AUX file 
166      * with the extension replaced by 
167      * the content of the group {@link #GRP_EXT_GLOSS}. 
168      * @param file
169      *    an AUX file from which the FileId is computed. 
170      * @param fileId
171      *    the empty FileId. 
172      * @return
173      *    the FileId created. 
174      * @throws IOException
175      */
176     FileId updateIdentGls(File file, FileId fileId) throws IOException {
177       // File parent = file.getParentFile();
178       // String inFile;
179       File glossFile;
180       try (BufferedReader bufferedReader =
181           new BufferedReader(new FileReader(file))) {
182         for (String line = bufferedReader.readLine(); line != null;
183             // readLine may thr. IOException
184             line = bufferedReader.readLine()) {
185           Matcher matcher = PATTERN_INPUT_GLOSSARY.matcher(line);
186           if (!matcher.find()) {
187             continue;
188           }
189           // Take all glossary files into account 
190           // which are created by makeglossaries 
191           fileId.update(line);
192           glossFile = TexFileUtils.replaceSuffix(file, 
193               "." + matcher.group(GRP_EXT_GLOSS));
194           updateIdent(glossFile, fileId);
195           // We assume all is top level 
196           // Matcher matcher = PATTERN_INPUT.matcher(line);
197           // if (matcher.find()) {
198           //   inFile = matcher.group(GRP_INPUT);
199           //   assert inFile.endsWith(this.extension());
200           //   // ignore return value 
201           //   updateIdentInclude(new File(parent, inFile), fileId, patternAux);
202           // }
203         } // for 
204       } // try 
205       return fileId;
206     }
207 
208     boolean process(LatexMainDesc desc, LatexProcessor proc)
209         throws BuildFailureException {
210       return proc.runMakeGlossary(desc);
211     }
212   },
213   Pyt {
214     String extension() {
215       return ".pytxcode";
216     }
217 
218     boolean mayBeEntryInToc() {
219       return false;
220     }
221 
222     boolean process(LatexMainDesc desc, LatexProcessor proc)
223         throws BuildFailureException {
224       return proc.runPythontex(desc);
225     }
226   };
227 
228   // maybe this is bad performance, scanning twice 
229   /**
230    * The pattern signifying 
231    * the presence of <code>bibdata</code> or <code>bibstyle</code> 
232    * in the top level AUX file
233    * indicating that <code>bibtex</code> must be run. 
234    * It is important, that one of the two commands is sufficient: 
235    * if only one is present, 
236    * or if <code>citation</code> is missing 
237    * (which may be in included AUX fies), 
238    * then running <code>bibtex</code> yields an error. 
239    * If neither <code>bibdata</code> nor <code>bibstyle</code> is present, 
240    * then <code>bibtex</code> is not run. 
241    * Then the latex engine yields a warning if a citation is present. 
242    * So either <code>bibdata</code> and <code>bibstyle</code> 
243    * and at least one citation is present, 
244    * or neither, or there is an error or a warning. 
245    */
246   private static final Pattern PATTERN_NEED_BIBTEX_RUN =
247       Pattern.compile("^\\\\(bibdata|bibstyle)");
248 
249   /**
250    * The pattern signifying the the presence of an `istfile`, 
251    * which has indeed ending <code>ist</code> 
252    * for makeglossaries configured with makeindex 
253    * but which is <code>xdy</code> for xindy. 
254    * This detail does not affect the general pattern. 
255    */
256   private static final Pattern PATTERN_NEED_MAKEGLOSSARIES_RUN =
257       Pattern.compile("^\\\\@istfilename");
258 
259   // filename with .aux
260   /**
261    * The pattern for iputting another AUX file 
262    * with a group with name {@link #GRP_INPUT} 
263    * comprising the name of the file with ending. 
264    */
265   private static final Pattern PATTERN_INPUT =
266       Pattern.compile("^\\\\@input\\{(?<fileName>.*)\\}");
267 
268   /**
269    * The name of the group in pattern {@link #PATTERN_INPUT} 
270    * comprising the filename with ending. 
271    */
272   private static final String GRP_INPUT = "fileName";
273 
274 
275   /**
276    * Pattern which holds in braces 
277    * the name of the glossary, 
278    * the extension of the log file and of the file created by the auxiliary tool, 
279    * and finally the extension of the file created by the package <code>glossaries</code>. 
280    * Only the latter goes into computation of the hash. 
281    * Example: <code>\@newglossary{main}{glg}{gls}{glo}</code>. 
282    */
283   private static final Pattern PATTERN_INPUT_GLOSSARY =
284       Pattern.compile("\\\\@newglossary\\{.+\\}\\{.+\\}\\{.+\\}\\{(?<fileExt>.*)\\}");
285 
286   /**
287    * The name of the group in pattern {@link #PATTERN_INPUT_GLOSSARY} 
288    * comprising the file extension of the file 
289    * created by a latex run by the package <code>glossaries</code> 
290    * holding the entries of the accoding glossary. 
291    */
292   private static final String GRP_EXT_GLOSS = "fileExt";
293 
294   /**
295    * The pattern of the aux file 
296    * read by <code>bibtex</code>. 
297    * This does not include 
298    * the pattern {@link #PATTERN_INPUT} 
299    * for inputting other aux files. 
300    * In fact only those patterns contributing to hashing. 
301    */
302   static final Pattern PATTERN_BIBTEX =
303       Pattern.compile("^\\\\(citation|bibstyle|bibdata)");
304 
305   /**
306    * The file extension <code>.ext</code> triggering the action. 
307    * If <code>xxx.tex</code> is the latex main file, 
308    * then <code>xxx.ext</code> is the triggering file. 
309    * This file is created fromt the TEX file, 
310    * in some cases due to some package. 
311    * 
312    * @return
313    *    The extension (with dot) as a string. 
314    */
315   // TBD: without dot would be more elegant 
316   abstract String extension();
317 
318   boolean doesFitAuxiliary(File file) {
319     return file.exists();
320   }
321 
322   // currently true except for Pyt 
323   boolean mayBeEntryInToc() {
324     return true;
325   }
326 
327   // must be non-negative 
328   int numRunsAfter() {
329     return 1;
330   }
331 
332   /**
333    * Try to process the file associated with this auxiliary. 
334    * @return
335    *    whether a processor has been invoked. 
336    */
337   abstract boolean process(LatexMainDesc desc, LatexProcessor proc)
338       throws BuildFailureException;
339 
340   // overwritten for bibtex and one time also for bib2gls
341   /**
342    * Yields the identifier 
343    * corresponding with the given text file. 
344    * It takes only the lines into account 
345    * which are relevant for this this {@link Auxiliary}. 
346    * Note that for some {@link Auxiliary}s, 
347    * typically tied to AUX files, 
348    * other files referred to are taken into account 
349    * as for {@link Auxiliary#BibTex}
350    * 
351    * @param file
352    *   a text file written by the part of the {@link Auxiliary} 
353    *   given by the run of the latex compiler 
354    *   and at least partially read by the corresponding auxiliary program. 
355    * @return
356    *   The file identifier taking all lines of <code>file</code> into account 
357    *   which are relevant for this {@link Auxiliary}. 
358    * @throws IOException
359    *    if the file or files referred to within it 
360    *    could not be read completely. 
361    *    This degrades rerun detection. 
362    */
363   FileId getIdent(File file) throws IOException {
364     return updateIdent(file, new FileId());
365   }
366 
367   FileId updateIdent(File file, FileId fileId) throws IOException {
368     //System.out.println("update:gen");
369     try (BufferedReader bufferedReader =
370         new BufferedReader(new FileReader(file))) {
371       for (String line = bufferedReader.readLine(); line != null;
372           // readLine may thr. IOException
373           line = bufferedReader.readLine()) {
374         fileId.update(line);
375       }
376     }
377     return fileId;
378   }
379 
380   // invoked by BibTex.getIdent(File), Glo.getIdent(File) (strange) 
381   // and by itself. 
382   FileId updateIdentInclude(File file, FileId fileId, Pattern patternAux) throws IOException {
383     File parent = file.getParentFile();
384     String inFile;
385     try (BufferedReader bufferedReader =
386         new BufferedReader(new FileReader(file))) {
387       for (String line = bufferedReader.readLine(); line != null;
388           // readLine may thr. IOException
389           line = bufferedReader.readLine()) {
390         if (patternAux.matcher(line).find()) {
391           fileId.update(line);
392           continue;
393         }
394         Matcher matcher = PATTERN_INPUT.matcher(line);
395         if (matcher.find()) {
396           inFile = matcher.group(GRP_INPUT);
397           assert inFile.endsWith(this.extension());
398           // ignore return value 
399           updateIdentInclude(new File(parent, inFile), fileId, patternAux);
400         }
401       } // for 
402     } // try 
403     return fileId;
404   }
405 
406   /**
407    * Returns whether this {@link Auxiliary} fits which is assumed 
408    * if the <code>pattern</code> matches any line in <code>file</code>. 
409    * This is used to implement {@link #doesFitAuxiliary(File)} 
410    * for auxiliaries {@link #BibTex} and {@link #Glo}. 
411    * 
412    * @param file
413    *    the file the name of which is the according latex main file 
414    *    endowed with ending given by {@link #extension()}. 
415    * @param pattern
416    *    The pattern to be matched to lines in <code>file</code>. 
417    *    For {@link #BibTex}{@link #doesFitAuxiliary(File)} 
418    *    this method is invoked with pattern {@link #PATTERN_NEED_BIBTEX_RUN}, 
419    *    whereas for {@link #Glo}, 
420    *    the pattern is {@link #PATTERN_NEED_MAKEGLOSSARIES_RUN}. 
421    * @return
422    *    Whether <code>pattern</code> matches any line in <code>file</code>. 
423    * @see #BibTex{@link #doesFitAuxiliary(File)}
424    * @see #Glo{@link #doesFitAuxiliary(File)}
425    */
426   boolean doesFitAuxiliary(File file, Pattern pattern) {
427     if (!file.exists()) {
428       return false;
429     }
430 
431     try (BufferedReader bufferedReader =
432         new BufferedReader(new FileReader(file))) {
433       for (String line = bufferedReader.readLine(); line != null;
434           // readLine may thr. IOException
435           line = bufferedReader.readLine()) {
436         if (pattern.matcher(line).find()) {
437           return true;
438         }
439       } // for 
440       return false;
441     } catch (IOException e) {
442       // TBD: add warning 
443       return true;
444     }
445   }
446 
447 }