View Javadoc
1   package org.djunits.generator;
2   
3   import java.io.BufferedReader;
4   import java.io.File;
5   import java.io.FileNotFoundException;
6   import java.io.FileReader;
7   import java.io.IOException;
8   import java.io.PrintWriter;
9   import java.net.URISyntaxException;
10  import java.net.URL;
11  import java.nio.file.Files;
12  import java.nio.file.Paths;
13  import java.time.Instant;
14  import java.util.ArrayList;
15  import java.util.HashMap;
16  import java.util.List;
17  import java.util.Map;
18  
19  /**
20   * <p>
21   * Copyright (c) 2013-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
22   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
23   * </p>
24   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
25   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
26   */
27  public class GenerateDJUNIT
28  {
29      /** The output folder of the writer -- will be written in Eclipse project-root/generated-code/org/djunits folder. */
30      private static final String generatedCodeRelativePath = "/generated-code/org/djunits/";
31  
32      /** the generation time. */
33      private static String generationTime;
34  
35      /**
36       * The calculated absolute output path (root of the executable or Jar file). In case of an Eclipse run, ../../ is added to
37       * the path to place the results in the root of the project, rather than in target/classes.
38       */
39      private static String absoluteRootPath;
40  
41      /** List of Abs + Rel types. */
42      private static List<String[]> typesAbsRel = new ArrayList<>();
43  
44      /** List of Rel types. */
45      private static List<String> typesRel = new ArrayList<>();
46  
47      /** Map of types to formulas. */
48      private static Map<String, List<String>> formulas = new HashMap<>();
49  
50      /** Map of replacement tags to replacement string. */
51      private static Map<String, String> replaceMap = new HashMap<>();
52  
53      /** Map of replacement tags to type for which the replacement has to be done. */
54      private static Map<String, String> replaceType = new HashMap<>();
55  
56      /**
57       * Read the types from the file /TYPES_ABS_REL.txt.
58       * @throws IOException on I/O error
59       */
60      private static void readAbsRelTypes() throws IOException
61      {
62          URL typesURL = URLResource.getResource("/resources/TYPES_ABS_REL.txt");
63          FileReader fileReader = new FileReader(new File(typesURL.getPath()));
64          BufferedReader bufferedReader = new BufferedReader(fileReader);
65          String line = null;
66          while ((line = bufferedReader.readLine()) != null)
67          {
68              if (line.length() > 0)
69              {
70                  typesAbsRel.add(line.split(","));
71              }
72          }
73          bufferedReader.close();
74      }
75  
76      /**
77       * Read the types from the file /TYPES_REL.txt.
78       * @throws IOException on I/O error
79       */
80      private static void readRelTypes() throws IOException
81      {
82          URL typesURL = URLResource.getResource("/resources/TYPES_REL.txt");
83          FileReader fileReader = new FileReader(new File(typesURL.getPath()));
84          BufferedReader bufferedReader = new BufferedReader(fileReader);
85          String line = null;
86          while ((line = bufferedReader.readLine()) != null)
87          {
88              if (line.length() > 0)
89              {
90                  typesRel.add(line);
91              }
92          }
93          bufferedReader.close();
94      }
95  
96      /**
97       * Read the formulas from the file /FORMULAS.txt.
98       * @throws IOException on I/O error
99       */
100     private static void readFormulas() throws IOException
101     {
102         URL typesURL = URLResource.getResource("/resources/FORMULAS.txt");
103         FileReader fileReader = new FileReader(new File(typesURL.getPath()));
104         BufferedReader bufferedReader = new BufferedReader(fileReader);
105         String type = null;
106         String line = null;
107         List<String> flist = new ArrayList<>();
108         while ((line = bufferedReader.readLine()) != null)
109         {
110             if (line.startsWith("%"))
111             {
112                 if (type != null)
113                 {
114                     formulas.put(type, flist);
115                 }
116                 type = line.replaceAll("%", "").trim();
117                 flist = new ArrayList<>();
118             }
119             else
120             {
121                 if (line.trim().length() > 0)
122                 {
123                     flist.add(line.trim());
124                 }
125             }
126         }
127         formulas.put(type, flist);
128         bufferedReader.close();
129     }
130 
131     /**
132      * Read the replacement strings from the file /REPLACE.txt.
133      * @throws IOException on I/O error
134      */
135     private static void readReplace() throws IOException
136     {
137         URL typesURL = URLResource.getResource("/resources/REPLACE.txt");
138         FileReader fileReader = new FileReader(new File(typesURL.getPath()));
139         BufferedReader bufferedReader = new BufferedReader(fileReader);
140         String tag = null;
141         String line = null;
142         String type = null;
143         String replacementLines = "";
144         while ((line = bufferedReader.readLine()) != null)
145         {
146             if (line.startsWith("##"))
147             {
148                 if (tag != null)
149                 {
150                     replaceMap.put(tag, replacementLines);
151                     replaceType.put(tag, type);
152                     tag = null;
153                 }
154                 else
155                 {
156                     tag = line.trim();
157                     type = bufferedReader.readLine();
158                     replacementLines = "";
159                 }
160             }
161             else
162             {
163                 replacementLines += line + "\n";
164             }
165         }
166         bufferedReader.close();
167     }
168 
169     /****************************************************************************************************************/
170     /********************************************* SCALAR ***********************************************************/
171     /****************************************************************************************************************/
172 
173     /**
174      * Insert formulas based on FORMULAS.txt into the %FORMULAS% marker within the Java file.
175      * @param java String; the java file
176      * @param errorType String; the type for error messaging
177      * @param prefix String; e.g., Float for Float types, or blank for Double types
178      * @param absolute boolean; to indicate it is an absolute type
179      * @return the file with replacements
180      */
181     private static String formulas(final String java, final String errorType, final String prefix, final boolean absolute)
182     {
183         String ret = java;
184         while (ret.contains("%FORMULAS%"))
185         {
186             int pos = ret.indexOf("%FORMULAS%");
187             ret = ret.replaceFirst("%FORMULAS%", "");
188             int end = ret.indexOf("%", pos);
189             if (end == -1)
190             {
191                 System.err.println("Closing % not found for %FORMULAS% in file for type " + errorType);
192                 return ret;
193             }
194             String type = ret.substring(pos, end);
195             String pType = prefix + type;
196             if (!formulas.containsKey(type))
197             {
198                 System.err.println("Formulas in FORMULAS.txt does not contain entry for type " + errorType);
199                 return ret.substring(0, pos - 1) + ret.substring(pos + type.length() + 2, ret.length() - 1);
200             }
201             String fStr = "";
202             String reciprocalType = null;
203             for (String f : formulas.get(type))
204             {
205                 String dm = f.startsWith("/") ? "division" : "multiplication";
206                 String method = f.startsWith("/") ? "divide" : "times";
207                 String mdsign = f.startsWith("/") ? "/" : "*";
208                 f = f.substring(1, f.length());
209                 String param = f.split("=")[0].trim();
210                 String result = f.split("=")[1].trim();
211                 String pParam = prefix + param;
212                 String pResult = prefix + result;
213 
214                 fStr += "        /**\n";
215                 fStr += "         * Calculate the " + dm + " of " + pType + " and " + pParam + ", which results in a ";
216                 fStr += pResult + " scalar.\n";
217                 fStr += "         * @param v " + pType + "; scalar\n";
218                 fStr += "         * @return " + pResult + "; scalar as a " + dm + " of " + pType + " and " + pParam + "\n";
219                 fStr += "         */\n";
220                 fStr += "        public final " + pResult + " " + method;
221                 fStr += "(final " + pParam + " v)\n";
222                 fStr += "        {\n";
223                 fStr += "            return new " + pResult + "(this.si " + mdsign + " v.si, ";
224                 fStr += result + "Unit.SI);\n";
225                 fStr += "        }\n\n";
226 
227                 if (result.equals("Dimensionless") && mdsign.equals("*"))
228                     reciprocalType = param;
229             }
230 
231             // reciprocal function
232             if (!absolute)
233             {
234                 fStr += "        /** {@inheritDoc} */\n";
235                 fStr += "        @Override\n";
236                 if (reciprocalType == null)
237                 {
238                     fStr += "        public " + prefix + "SIScalar reciprocal()\n";
239                     fStr += "        {\n";
240                     String df = prefix.length() == 0 ? "Double" : "Float";
241                     fStr += "            return " + df + "Scalar.divide(" + prefix + "Dimensionless.ONE, this);\n";
242                 }
243                 else
244                 {
245                     fStr += "        public " + prefix + reciprocalType + " reciprocal()\n";
246                     fStr += "        {\n";
247                     String f = prefix.length() == 0 ? "" : "f";
248                     fStr += "            return " + prefix + reciprocalType + ".instantiateSI(1.0" + f + " / this.si);\n";
249                 }
250                 fStr += "        }\n\n";
251             }
252 
253             ret = ret.substring(0, pos - 1) + fStr + ret.substring(pos + type.length() + 1, ret.length() - 1);
254         }
255         return ret;
256     }
257 
258     /**
259      * Insert replacements based on REPLACCE.txt into the Java file.
260      * @param java String; the file
261      * @param type String; the type
262      * @return the file with replacements
263      */
264     private static String replace(String java, final String type)
265     {
266         // replace the "replacement" tags
267         for (String tag : replaceMap.keySet())
268         {
269             String replacement = (replaceType.get(tag).equals(type)) ? replaceMap.get(tag) : "";
270             while (java.contains(tag))
271             {
272                 java = java.replace(tag, replacement);
273             }
274         }
275 
276         // @Generated
277         java = java.replace("@Generated(value = \"GenerateDJUNIT\")",
278                 "@Generated(value = \"" + GenerateDJUNIT.class.getName() + "\", date = \"" + generationTime + "\")");
279 
280         return java;
281     }
282 
283     /**
284      * Replace the %TypeAbs%, %TypeRel%, %TypeAbsUnit%, and %TypeRelUnit% tags in the java string.
285      * @param in String; the original java string
286      * @param type String[]; the types: [abs, rel, unit]
287      * @return the java string with replacements
288      */
289     private static String replaceAbsRel(final String in, final String[] type)
290     {
291         String typeAbs = type[0];
292         String typeRel = type[1];
293         String typeAbsUnit = type[2];
294         String typeRelUnit = type[3];
295         String java = in.replaceAll("%TypeAbs%", typeAbs);
296         java = java.replaceAll("%typeabs%", typeAbs.toLowerCase());
297         java = java.replaceAll("%TYPEABS%", typeAbs.toUpperCase());
298         java = java.replaceAll("%TypeRel%", typeRel);
299         java = java.replaceAll("%typerel%", typeRel.toLowerCase());
300         java = java.replaceAll("%TYPEREL%", typeRel.toUpperCase());
301         java = java.replaceAll("%TypeAbsUnit%", typeAbsUnit);
302         java = java.replaceAll("%typeabsunit%", typeAbsUnit.toLowerCase());
303         java = java.replaceAll("%TYPEABSUNIT%", typeAbsUnit.toUpperCase());
304         java = java.replaceAll("%TypeRelUnit%", typeRelUnit);
305         java = java.replaceAll("%typerelunit%", typeRelUnit.toLowerCase());
306         java = java.replaceAll("%TYPERELUNIT%", typeRelUnit.toUpperCase());
307         return java;
308     }
309 
310     /**
311      * Generate all Abs + Rel classes in value.vdouble.scalar.
312      * @throws IOException on I/O error
313      * @throws URISyntaxException when file could not be found on the file system
314      */
315     private static void generateDoubleScalarAbsRel() throws IOException, URISyntaxException
316     {
317         for (int i = 0; i <= 1; i++)
318         {
319             String relativePath = "value/vdouble/scalar/";
320             URL scalarURL = URLResource.getResource(
321                     "/resources/" + relativePath + ((i == 0) ? "DOUBLE_SCALAR_AR_ABS.java" : "DOUBLE_SCALAR_AR_REL.java"));
322             String scalarJava = new String(Files.readAllBytes(Paths.get(scalarURL.toURI())));
323 
324             for (String[] type : typesAbsRel)
325             {
326                 File outPath = new File(absoluteRootPath + relativePath);
327                 outPath.mkdirs();
328                 PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + type[i] + ".java");
329                 String java = new String(scalarJava);
330                 java = replaceAbsRel(java, type);
331                 java = formulas(java, "DoubleScalar => " + type[i], "", i == 0);
332                 java = replace(java, type[i]);
333                 out.print(java);
334                 out.close();
335                 System.out.println("built: " + absoluteRootPath + relativePath + type[i] + ".java");
336             }
337         }
338     }
339 
340     /**
341      * Generate all Rel classes in value.vdouble.scalar.
342      * @throws IOException on I/O error
343      * @throws URISyntaxException when file could not be found on the file system
344      */
345     private static void generateDoubleScalarRel() throws IOException, URISyntaxException
346     {
347         String relativePath = "value/vdouble/scalar/";
348         URL scalarURL = URLResource.getResource("/resources/" + relativePath + "DOUBLE_SCALAR_REL.java");
349         String scalarJava = new String(Files.readAllBytes(Paths.get(scalarURL.toURI())));
350 
351         for (String type : typesRel)
352         {
353             File outPath = new File(absoluteRootPath + relativePath);
354             outPath.mkdirs();
355             PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + type + ".java");
356             String java = new String(scalarJava);
357             java = java.replaceAll("%Type%", type);
358             java = java.replaceAll("%type%", type.toLowerCase());
359             java = java.replaceAll("%TYPE%", type.toUpperCase());
360             if (java.contains("class Dimensionless"))
361             {
362                 java = java.replace("%DIMLESS%", "implements DimensionlessFunctions<DimensionlessUnit, Dimensionless>");
363                 URL dimlessURL = URLResource.getResource("/resources/" + relativePath + "DimlessFunctions.java");
364                 String dimlessFunctions = new String(Files.readAllBytes(Paths.get(dimlessURL.toURI())));
365                 int pos = java.indexOf("%FORMULAS%");
366                 java = java.substring(0, pos - 1) + dimlessFunctions + java.substring(pos, java.length() - 1);
367             }
368             java = java.replace("%DIMLESS%", "");
369             java = formulas(java, "DoubleScalar => " + type, "", false);
370             java = replace(java, type);
371             out.print(java);
372             out.close();
373             System.out.println("built: " + absoluteRootPath + relativePath + type + ".java");
374         }
375     }
376 
377     /**
378      * Generate all Abs + Rel classes in value.vfloat.scalar.
379      * @throws IOException on I/O error
380      * @throws URISyntaxException when file could not be found on the file system
381      */
382     private static void generateFloatScalarAbsRel() throws IOException, URISyntaxException
383     {
384         for (int i = 0; i <= 1; i++)
385         {
386             String relativePath = "value/vfloat/scalar/";
387             URL scalarURL = URLResource.getResource(
388                     "/resources/" + relativePath + ((i == 0) ? "FLOAT_SCALAR_AR_ABS.java" : "FLOAT_SCALAR_AR_REL.java"));
389             String scalarJava = new String(Files.readAllBytes(Paths.get(scalarURL.toURI())));
390 
391             for (String type[] : typesAbsRel)
392             {
393                 String fType = "Float" + type[i];
394                 File outPath = new File(absoluteRootPath + relativePath);
395                 outPath.mkdirs();
396                 PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + fType + ".java");
397                 String java = new String(scalarJava);
398                 java = replaceAbsRel(java, type);
399                 java = formulas(java, "FloatScalar => " + type[i], "Float", i == 0);
400                 java = replace(java, type[i]);
401                 out.print(java);
402                 out.close();
403                 System.out.println("built: " + absoluteRootPath + relativePath + fType + ".java");
404             }
405         }
406     }
407 
408     /**
409      * Generate all Rel classes in value.vfloat.scalar.
410      * @throws IOException on I/O error
411      * @throws URISyntaxException when file could not be found on the file system
412      */
413     private static void generateFloatScalarRel() throws IOException, URISyntaxException
414     {
415         String relativePath = "value/vfloat/scalar/";
416         URL scalarURL = URLResource.getResource("/resources/" + relativePath + "FLOAT_SCALAR_REL.java");
417         String scalarJava = new String(Files.readAllBytes(Paths.get(scalarURL.toURI())));
418 
419         for (String type : typesRel)
420         {
421             String fType = "Float" + type;
422             File outPath = new File(absoluteRootPath + relativePath);
423             outPath.mkdirs();
424             PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + fType + ".java");
425             String java = new String(scalarJava);
426             java = java.replaceAll("%Type%", type);
427             java = java.replaceAll("%type%", type.toLowerCase());
428             java = java.replaceAll("%TYPE%", type.toUpperCase());
429             if (java.contains("class FloatDimensionless"))
430             {
431                 java = java.replace("%DIMLESS%", " implements DimensionlessFunctions<DimensionlessUnit, FloatDimensionless>");
432                 URL dimlessURL = URLResource.getResource("/resources/" + relativePath + "DimlessFunctions.java");
433                 String dimlessFunctions = new String(Files.readAllBytes(Paths.get(dimlessURL.toURI())));
434                 int pos = java.indexOf("%FORMULAS%");
435                 java = java.substring(0, pos - 1) + dimlessFunctions + java.substring(pos, java.length() - 1);
436             }
437             java = java.replace("%DIMLESS%", "");
438             java = formulas(java, "FloatScalar => " + type, "Float", false);
439             java = replace(java, type);
440             out.print(java);
441             out.close();
442             System.out.println("built: " + absoluteRootPath + relativePath + fType + ".java");
443         }
444     }
445 
446     /****************************************************************************************************************/
447     /************************************************ VECTOR ********************************************************/
448     /****************************************************************************************************************/
449 
450     /**
451      * Insert formulas based on FORMULAS.txt into the %FORMULAS% marker within the Java file.
452      * @param java String; the java file
453      * @param errorType String; the type for error messaging
454      * @param prefix String; e.g., Float for Float types, or blank for Double types
455      * @return the file with replacements
456      */
457     private static String formulasVector(final String java, final String errorType, final String prefix)
458     {
459         String ret = java;
460         while (ret.contains("%FORMULAS%"))
461         {
462             int pos = ret.indexOf("%FORMULAS%");
463             ret = ret.replaceFirst("%FORMULAS%", "");
464             int end = ret.indexOf("%", pos);
465             if (end == -1)
466             {
467                 System.err.println("Closing % not found for %FORMULAS% in file for type " + errorType);
468                 return ret;
469             }
470             String type = ret.substring(pos, end);
471             if (!formulas.containsKey(type))
472             {
473                 System.err.println("Formulas in FORMULAS.txt does not contain entry for type " + errorType);
474                 return ret.substring(0, pos - 1) + ret.substring(pos + type.length() + 2, ret.length() - 1);
475             }
476             String fStr = "";
477             ret = ret.substring(0, pos - 1) + fStr + ret.substring(pos + type.length() + 1, ret.length() - 1);
478         }
479         return ret;
480     }
481 
482     /****************************************************************************************************************/
483     /********************************************* DOUBLEVECTOR *****************************************************/
484     /****************************************************************************************************************/
485 
486     /**
487      * Generate all Abs + Rel classes in value.vdouble.vector.
488      * @throws IOException on I/O error
489      * @throws URISyntaxException when file could not be found on the file system
490      */
491     private static void generateDoubleVectorAbsRel() throws IOException, URISyntaxException
492     {
493         for (int i = 0; i <= 1; i++)
494         {
495             String relativePath = "value/vdouble/vector/";
496             URL vectorURL = URLResource.getResource(
497                     "/resources/" + relativePath + ((i == 0) ? "DOUBLE_VECTOR_AR_ABS.java" : "DOUBLE_VECTOR_AR_REL.java"));
498             String vectorJava = new String(Files.readAllBytes(Paths.get(vectorURL.toURI())));
499 
500             for (String type[] : typesAbsRel)
501             {
502                 File outPath = new File(absoluteRootPath + relativePath);
503                 outPath.mkdirs();
504                 PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + type[i] + "Vector.java");
505                 String java = new String(vectorJava);
506                 java = replaceAbsRel(java, type);
507                 java = formulasVector(java, "DoubleVector => " + type[i], "");
508                 java = replace(java, type[i]);
509                 out.print(java);
510                 out.close();
511                 System.out.println("built: " + absoluteRootPath + relativePath + type[i] + "Vector.java");
512             }
513         }
514     }
515 
516     /**
517      * Generate all Rel classes in value.vdouble.vector.
518      * @throws IOException on I/O error
519      * @throws URISyntaxException when file could not be found on the file system
520      */
521     private static void generateDoubleVectorRel() throws IOException, URISyntaxException
522     {
523         String relativePath = "value/vdouble/vector/";
524         URL vectorURL = URLResource.getResource("/resources/" + relativePath + "DOUBLE_VECTOR_REL.java");
525         String vectorJava = new String(Files.readAllBytes(Paths.get(vectorURL.toURI())));
526 
527         for (String type : typesRel)
528         {
529             File outPath = new File(absoluteRootPath + relativePath);
530             outPath.mkdirs();
531             PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + type + "Vector.java");
532             String java = new String(vectorJava);
533             java = java.replaceAll("%Type%", type);
534             java = java.replaceAll("%type%", type.toLowerCase());
535             java = java.replaceAll("%TYPE%", type.toUpperCase());
536             if (java.contains("class DimensionlessVector"))
537             {
538                 java = java.replace("%DIMLESS%",
539                         " implements DoubleMathFunctions, DimensionlessFunctions<DimensionlessUnit, DimensionlessVector>");
540                 URL dimlessURL = URLResource.getResource("/resources/" + relativePath + "DimlessFunctions.java");
541                 String dimlessFunctions = new String(Files.readAllBytes(Paths.get(dimlessURL.toURI())));
542                 int pos = java.indexOf("%FORMULAS%");
543                 java = java.substring(0, pos - 1) + dimlessFunctions + java.substring(pos, java.length() - 1);
544             }
545             java = java.replace("%DIMLESS%", "");
546             java = formulasVector(java, "DoubleVector => " + type, "");
547             java = replace(java, type);
548             out.print(java);
549             out.close();
550             System.out.println("built: " + absoluteRootPath + relativePath + type + "Vector.java");
551         }
552     }
553 
554     /****************************************************************************************************************/
555     /********************************************** FLOATVECTOR *****************************************************/
556     /****************************************************************************************************************/
557 
558     /**
559      * Generate all Abs + Rel classes in value.vfloat.vector.
560      * @throws IOException on I/O error
561      * @throws URISyntaxException when file could not be found on the file system
562      */
563     private static void generateFloatVectorAbsRel() throws IOException, URISyntaxException
564     {
565         for (int i = 0; i <= 1; i++)
566         {
567             String relativePath = "value/vfloat/vector/";
568             URL vectorURL = URLResource.getResource(
569                     "/resources/" + relativePath + ((i == 0) ? "FLOAT_VECTOR_AR_ABS.java" : "FLOAT_VECTOR_AR_REL.java"));
570             String vectorJava = new String(Files.readAllBytes(Paths.get(vectorURL.toURI())));
571 
572             for (String type[] : typesAbsRel)
573             {
574                 String fType = "Float" + type[i];
575                 File outPath = new File(absoluteRootPath + relativePath);
576                 outPath.mkdirs();
577                 PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + fType + "Vector.java");
578                 String java = new String(vectorJava);
579                 java = replaceAbsRel(java, type);
580                 java = formulasVector(java, "FloatVector => " + fType, "");
581                 java = replace(java, type[i]);
582                 out.print(java);
583                 out.close();
584                 System.out.println("built: " + absoluteRootPath + relativePath + fType + "Vector.java");
585             }
586         }
587     }
588 
589     /**
590      * Generate all Rel classes in value.vfloat.vector.
591      * @throws IOException on I/O error
592      * @throws URISyntaxException when file could not be found on the file system
593      */
594     private static void generateFloatVectorRel() throws IOException, URISyntaxException
595     {
596         String relativePath = "value/vfloat/vector/";
597         URL vectorURL = URLResource.getResource("/resources/" + relativePath + "FLOAT_VECTOR_REL.java");
598         String vectorJava = new String(Files.readAllBytes(Paths.get(vectorURL.toURI())));
599 
600         for (String type : typesRel)
601         {
602             String fType = "Float" + type;
603             File outPath = new File(absoluteRootPath + relativePath);
604             outPath.mkdirs();
605             PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + fType + "Vector.java");
606             String java = new String(vectorJava);
607             java = java.replaceAll("%Type%", type);
608             java = java.replaceAll("%type%", type.toLowerCase());
609             java = java.replaceAll("%TYPE%", type.toUpperCase());
610             if (java.contains("class FloatDimensionlessVector"))
611             {
612                 java = java.replace("%DIMLESS%",
613                         " implements DimensionlessFunctions<DimensionlessUnit, FloatDimensionlessVector>");
614                 URL dimlessURL = URLResource.getResource("/resources/" + relativePath + "DimlessFunctions.java");
615                 String dimlessFunctions = new String(Files.readAllBytes(Paths.get(dimlessURL.toURI())));
616                 int pos = java.indexOf("%FORMULAS%");
617                 java = java.substring(0, pos - 1) + dimlessFunctions + java.substring(pos, java.length() - 1);
618             }
619             java = java.replace("%DIMLESS%", "");
620             java = formulasVector(java, "FloatVector => " + fType, "");
621             java = replace(java, type);
622             out.print(java);
623             out.close();
624             System.out.println("built: " + absoluteRootPath + relativePath + fType + "Vector.java");
625         }
626     }
627 
628     /****************************************************************************************************************/
629     /************************************************ MATRIX ********************************************************/
630     /****************************************************************************************************************/
631 
632     /**
633      * Insert formulas based on FORMULAS.txt into the %FORMULAS% marker within the Java file.
634      * @param java String; the java file
635      * @param errorType String; the type for error messaging
636      * @param prefix String; e.g., Float for Float types, or blank for Double types
637      * @return the file with replacements
638      */
639     private static String formulasMatrix(final String java, final String errorType, final String prefix)
640     {
641         String ret = java;
642         while (ret.contains("%FORMULAS%"))
643         {
644             int pos = ret.indexOf("%FORMULAS%");
645             ret = ret.replaceFirst("%FORMULAS%", "");
646             int end = ret.indexOf("%", pos);
647             if (end == -1)
648             {
649                 System.err.println("Closing % not found for %FORMULAS% in file for type " + errorType);
650                 return ret;
651             }
652             String type = ret.substring(pos, end);
653             if (!formulas.containsKey(type))
654             {
655                 System.err.println("Formulas in FORMULAS.txt does not contain entry for type " + errorType);
656                 return ret.substring(0, pos - 1) + ret.substring(pos + type.length() + 2, ret.length() - 1);
657             }
658             String fStr = "";
659             ret = ret.substring(0, pos - 1) + fStr + ret.substring(pos + type.length() + 1, ret.length() - 1);
660         }
661         return ret;
662     }
663 
664     /****************************************************************************************************************/
665     /********************************************* DOUBLEMATRIX *****************************************************/
666     /****************************************************************************************************************/
667 
668     /**
669      * Generate all Abs + Rel classes in value.vdouble.matrix.
670      * @throws IOException on I/O error
671      * @throws URISyntaxException when file could not be found on the file system
672      */
673     private static void generateDoubleMatrixAbsRel() throws IOException, URISyntaxException
674     {
675         for (int i = 0; i <= 1; i++)
676         {
677             String relativePath = "value/vdouble/matrix/";
678             URL matrixURL = URLResource.getResource(
679                     "/resources/" + relativePath + ((i == 0) ? "DOUBLE_MATRIX_AR_ABS.java" : "DOUBLE_MATRIX_AR_REL.java"));
680             String matrixJava = new String(Files.readAllBytes(Paths.get(matrixURL.toURI())));
681 
682             for (String[] type : typesAbsRel)
683             {
684                 File outPath = new File(absoluteRootPath + relativePath);
685                 outPath.mkdirs();
686                 PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + type[i] + "Matrix.java");
687                 String java = new String(matrixJava);
688                 java = replaceAbsRel(java, type);
689                 java = formulasMatrix(java, "DoubleMatrix => " + type[i], "");
690                 java = replace(java, type[i]);
691                 out.print(java);
692                 out.close();
693                 System.out.println("built: " + absoluteRootPath + relativePath + type[i] + "Matrix.java");
694             }
695         }
696     }
697 
698     /**
699      * Generate all Rel classes in value.vdouble.matrix.
700      * @throws IOException on I/O error
701      * @throws URISyntaxException when file could not be found on the file system
702      */
703     private static void generateDoubleMatrixRel() throws IOException, URISyntaxException
704     {
705         String relativePath = "value/vdouble/matrix/";
706         URL matrixURL = URLResource.getResource("/resources/" + relativePath + "DOUBLE_MATRIX_REL.java");
707         String matrixJava = new String(Files.readAllBytes(Paths.get(matrixURL.toURI())));
708 
709         for (String type : typesRel)
710         {
711             File outPath = new File(absoluteRootPath + relativePath);
712             outPath.mkdirs();
713             PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + type + "Matrix.java");
714             String java = new String(matrixJava);
715             java = java.replaceAll("%Type%", type);
716             java = java.replaceAll("%type%", type.toLowerCase());
717             java = java.replaceAll("%TYPE%", type.toUpperCase());
718             if (java.contains("class DimensionlessMatrix"))
719             {
720                 java = java.replace("%DIMLESS%", " implements DimensionlessFunctions<DimensionlessUnit, DimensionlessMatrix>");
721                 URL dimlessURL = URLResource.getResource("/resources/" + relativePath + "DimlessFunctions.java");
722                 String dimlessFunctions = new String(Files.readAllBytes(Paths.get(dimlessURL.toURI())));
723                 int pos = java.indexOf("%FORMULAS%");
724                 java = java.substring(0, pos - 1) + dimlessFunctions + java.substring(pos, java.length() - 1);
725             }
726             java = java.replace("%DIMLESS%", "");
727             java = formulasMatrix(java, "DoubleMatrix => " + type, "");
728             java = replace(java, type);
729             out.print(java);
730             out.close();
731             System.out.println("built: " + absoluteRootPath + relativePath + type + "Matrix.java");
732         }
733     }
734 
735     /****************************************************************************************************************/
736     /********************************************** FLOATMATRIX *****************************************************/
737     /****************************************************************************************************************/
738 
739     /**
740      * Generate all Abs + Rel classes in value.vfloat.matrix.
741      * @throws IOException on I/O error
742      * @throws URISyntaxException when file could not be found on the file system
743      */
744     private static void generateFloatMatrixAbsRel() throws IOException, URISyntaxException
745     {
746         for (int i = 0; i <= 1; i++)
747         {
748             String relativePath = "value/vfloat/matrix/";
749             URL matrixURL = URLResource.getResource(
750                     "/resources/" + relativePath + ((i == 0) ? "FLOAT_MATRIX_AR_ABS.java" : "FLOAT_MATRIX_AR_REL.java"));
751             String matrixJava = new String(Files.readAllBytes(Paths.get(matrixURL.toURI())));
752 
753             for (String type[] : typesAbsRel)
754             {
755                 String fType = "Float" + type[i];
756                 File outPath = new File(absoluteRootPath + relativePath);
757                 outPath.mkdirs();
758                 PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + fType + "Matrix.java");
759                 String java = new String(matrixJava);
760                 java = replaceAbsRel(java, type);
761                 java = formulasMatrix(java, "FloatMatrix => " + fType, "");
762                 java = replace(java, type[i]);
763                 out.print(java);
764                 out.close();
765                 System.out.println("built: " + absoluteRootPath + relativePath + fType + "Matrix.java");
766             }
767         }
768     }
769 
770     /**
771      * Generate all Rel classes in value.vfloat.matrix.
772      * @throws IOException on I/O error
773      * @throws URISyntaxException when file could not be found on the file system
774      */
775     private static void generateFloatMatrixRel() throws IOException, URISyntaxException
776     {
777         String relativePath = "value/vfloat/matrix/";
778         URL matrixURL = URLResource.getResource("/resources/" + relativePath + "FLOAT_MATRIX_REL.java");
779         String matrixJava = new String(Files.readAllBytes(Paths.get(matrixURL.toURI())));
780 
781         for (String type : typesRel)
782         {
783             String fType = "Float" + type;
784             File outPath = new File(absoluteRootPath + relativePath);
785             outPath.mkdirs();
786             PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + fType + "Matrix.java");
787             String java = new String(matrixJava);
788             java = java.replaceAll("%Type%", type);
789             java = java.replaceAll("%type%", type.toLowerCase());
790             java = java.replaceAll("%TYPE%", type.toUpperCase());
791             if (java.contains("class FloatDimensionlessMatrix"))
792             {
793                 java = java.replace("%DIMLESS%",
794                         " implements DimensionlessFunctions<DimensionlessUnit, FloatDimensionlessMatrix>");
795                 URL dimlessURL = URLResource.getResource("/resources/" + relativePath + "DimlessFunctions.java");
796                 String dimlessFunctions = new String(Files.readAllBytes(Paths.get(dimlessURL.toURI())));
797                 int pos = java.indexOf("%FORMULAS%");
798                 java = java.substring(0, pos - 1) + dimlessFunctions + java.substring(pos, java.length() - 1);
799             }
800             java = java.replace("%DIMLESS%", "");
801             java = formulasMatrix(java, "FloatMatrix => " + fType, "");
802             java = replace(java, type);
803             out.print(java);
804             out.close();
805             System.out.println("built: " + absoluteRootPath + relativePath + fType + "Matrix.java");
806         }
807     }
808 
809     /****************************************************************************************************************/
810     /********************************************* SISCALAR *********************************************************/
811     /****************************************************************************************************************/
812 
813     /**
814      * Generate SIScalar.java in value.vdouble.scalar.
815      * @throws IOException on I/O error
816      * @throws URISyntaxException when file could not be found on the file system
817      */
818     private static void generateSIScalar() throws IOException, URISyntaxException
819     {
820         String relativePath = "value/vdouble/scalar/";
821         URL siScalarURL = URLResource.getResource("/resources/" + relativePath + "SISCALAR.java");
822         String siJava = new String(Files.readAllBytes(Paths.get(siScalarURL.toURI())));
823         siJava = siJava.replace("@Generated(value = \"GenerateDJUNIT\")",
824                 "@Generated(value = \"" + GenerateDJUNIT.class.getName() + "\", date = \"" + generationTime + "\")");
825         String asJava = new String(Files
826                 .readAllBytes(Paths.get(URLResource.getResource("/resources/" + relativePath + "SISCALAR_AS.java").toURI())));
827 
828         List<String> allRelTypes = new ArrayList<>(typesRel);
829         for (String[] arType : typesAbsRel)
830         {
831             allRelTypes.add(arType[1]);
832         }
833 
834         String asMethods = "";
835         for (String type : allRelTypes)
836         {
837             String lc = type.toLowerCase();
838             asMethods += asJava.replaceAll("%Type%", type).replaceAll("%type%", lc);
839         }
840 
841         File outPath = new File(absoluteRootPath + relativePath);
842         outPath.mkdirs();
843         PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + "SIScalar.java");
844         String java = siJava.replace("%%ASMETHODS%%", asMethods);
845         out.print(java);
846         out.close();
847         System.out.println("built: " + absoluteRootPath + relativePath + "SIScalar.java");
848     }
849 
850     /**
851      * Generate SIScalar.java in value.vfloat.scalar.
852      * @throws IOException on I/O error
853      * @throws URISyntaxException when file could not be found on the file system
854      */
855     private static void generateFloatSIScalar() throws IOException, URISyntaxException
856     {
857         String relativePath = "value/vfloat/scalar/";
858         URL siScalarURL = URLResource.getResource("/resources/" + relativePath + "FLOATSISCALAR.java");
859         String siJava = new String(Files.readAllBytes(Paths.get(siScalarURL.toURI())));
860         siJava = siJava.replace("@Generated(value = \"GenerateDJUNIT\")",
861                 "@Generated(value = \"" + GenerateDJUNIT.class.getName() + "\", date = \"" + generationTime + "\")");
862         String asJava = new String(Files.readAllBytes(
863                 Paths.get(URLResource.getResource("/resources/" + relativePath + "FLOATSISCALAR_AS.java").toURI())));
864 
865         List<String> allRelTypes = new ArrayList<>(typesRel);
866         for (String[] arType : typesAbsRel)
867         {
868             allRelTypes.add(arType[1]);
869         }
870 
871         String asMethods = "";
872         for (String type : allRelTypes)
873         {
874             String lc = type.toLowerCase();
875             asMethods += asJava.replaceAll("%Type%", type).replaceAll("%type%", lc);
876         }
877 
878         File outPath = new File(absoluteRootPath + relativePath);
879         outPath.mkdirs();
880         PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + "FloatSIScalar.java");
881         String java = siJava.replace("%%ASMETHODS%%", asMethods);
882         out.print(java);
883         out.close();
884         System.out.println("built: " + absoluteRootPath + relativePath + "FloatSIScalar.java");
885     }
886 
887     /****************************************************************************************************************/
888     /********************************************* SIVECTOR *********************************************************/
889     /****************************************************************************************************************/
890 
891     /**
892      * Generate SIVector.java in value.vdouble.vector.
893      * @throws IOException on I/O error
894      * @throws URISyntaxException when file could not be found on the file system
895      */
896     private static void generateSIVector() throws IOException, URISyntaxException
897     {
898         String relativePath = "value/vdouble/vector/";
899         URL siVectorURL = URLResource.getResource("/resources/" + relativePath + "SIVECTOR.java");
900         String siJava = new String(Files.readAllBytes(Paths.get(siVectorURL.toURI())));
901         siJava = siJava.replace("@Generated(value = \"GenerateDJUNIT\")",
902                 "@Generated(value = \"" + GenerateDJUNIT.class.getName() + "\", date = \"" + generationTime + "\")");
903         String asJava = new String(Files
904                 .readAllBytes(Paths.get(URLResource.getResource("/resources/" + relativePath + "SIVECTOR_AS.java").toURI())));
905 
906         List<String> allRelTypes = new ArrayList<>(typesRel);
907         for (String[] arType : typesAbsRel)
908         {
909             allRelTypes.add(arType[1]);
910         }
911 
912         String asMethods = "";
913         for (String type : allRelTypes)
914         {
915             String lc = type.toLowerCase();
916             asMethods += asJava.replaceAll("%Type%", type).replaceAll("%type%", lc);
917         }
918 
919         File outPath = new File(absoluteRootPath + relativePath);
920         outPath.mkdirs();
921         PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + "SIVector.java");
922         String java = siJava.replace("%%ASMETHODS%%", asMethods);
923         out.print(java);
924         out.close();
925         System.out.println("built: " + absoluteRootPath + relativePath + "SIVector.java");
926     }
927 
928     /**
929      * Generate SIVector.java in value.vfloat.vector.
930      * @throws IOException on I/O error
931      * @throws URISyntaxException when file could not be found on the file system
932      */
933     private static void generateFloatSIVector() throws IOException, URISyntaxException
934     {
935         String relativePath = "value/vfloat/vector/";
936         URL siVectorURL = URLResource.getResource("/resources/" + relativePath + "FLOATSIVECTOR.java");
937         String siJava = new String(Files.readAllBytes(Paths.get(siVectorURL.toURI())));
938         siJava = siJava.replace("@Generated(value = \"GenerateDJUNIT\")",
939                 "@Generated(value = \"" + GenerateDJUNIT.class.getName() + "\", date = \"" + generationTime + "\")");
940         String asJava = new String(Files.readAllBytes(
941                 Paths.get(URLResource.getResource("/resources/" + relativePath + "FLOATSIVECTOR_AS.java").toURI())));
942 
943         List<String> allRelTypes = new ArrayList<>(typesRel);
944         for (String[] arType : typesAbsRel)
945         {
946             allRelTypes.add(arType[1]);
947         }
948 
949         String asMethods = "";
950         for (String type : allRelTypes)
951         {
952             String lc = type.toLowerCase();
953             asMethods += asJava.replaceAll("%Type%", type).replaceAll("%type%", lc);
954         }
955 
956         File outPath = new File(absoluteRootPath + relativePath);
957         outPath.mkdirs();
958         PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + "FloatSIVector.java");
959         String java = siJava.replace("%%ASMETHODS%%", asMethods);
960         out.print(java);
961         out.close();
962         System.out.println("built: " + absoluteRootPath + relativePath + "FloatSIVector.java");
963     }
964 
965     /****************************************************************************************************************/
966     /********************************************* SIMATRIX *********************************************************/
967     /****************************************************************************************************************/
968 
969     /**
970      * Generate SIMatrix.java in value.vdouble.matrix.
971      * @throws IOException on I/O error
972      * @throws URISyntaxException when file could not be found on the file system
973      */
974     private static void generateSIMatrix() throws IOException, URISyntaxException
975     {
976         String relativePath = "value/vdouble/matrix/";
977         URL siMatrixURL = URLResource.getResource("/resources/" + relativePath + "SIMATRIX.java");
978         String siJava = new String(Files.readAllBytes(Paths.get(siMatrixURL.toURI())));
979         siJava = siJava.replace("@Generated(value = \"GenerateDJUNIT\")",
980                 "@Generated(value = \"" + GenerateDJUNIT.class.getName() + "\", date = \"" + generationTime + "\")");
981         String asJava = new String(Files
982                 .readAllBytes(Paths.get(URLResource.getResource("/resources/" + relativePath + "SIMATRIX_AS.java").toURI())));
983 
984         List<String> allRelTypes = new ArrayList<>(typesRel);
985         for (String[] arType : typesAbsRel)
986         {
987             allRelTypes.add(arType[1]);
988         }
989 
990         String asMethods = "";
991         for (String type : allRelTypes)
992         {
993             String lc = type.toLowerCase();
994             asMethods += asJava.replaceAll("%Type%", type).replaceAll("%type%", lc);
995         }
996 
997         File outPath = new File(absoluteRootPath + relativePath);
998         outPath.mkdirs();
999         PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + "SIMatrix.java");
1000         String java = siJava.replace("%%ASMETHODS%%", asMethods);
1001         out.print(java);
1002         out.close();
1003         System.out.println("built: " + absoluteRootPath + relativePath + "SIMatrix.java");
1004     }
1005 
1006     /**
1007      * Generate SIMatrix.java in value.vfloat.matrix.
1008      * @throws IOException on I/O error
1009      * @throws URISyntaxException when file could not be found on the file system
1010      */
1011     private static void generateFloatSIMatrix() throws IOException, URISyntaxException
1012     {
1013         String relativePath = "value/vfloat/matrix/";
1014         URL siMatrixURL = URLResource.getResource("/resources/" + relativePath + "FLOATSIMATRIX.java");
1015         String siJava = new String(Files.readAllBytes(Paths.get(siMatrixURL.toURI())));
1016         siJava = siJava.replace("@Generated(value = \"GenerateDJUNIT\")",
1017                 "@Generated(value = \"" + GenerateDJUNIT.class.getName() + "\", date = \"" + generationTime + "\")");
1018         String asJava = new String(Files.readAllBytes(
1019                 Paths.get(URLResource.getResource("/resources/" + relativePath + "FLOATSIMATRIX_AS.java").toURI())));
1020 
1021         List<String> allRelTypes = new ArrayList<>(typesRel);
1022         for (String[] arType : typesAbsRel)
1023         {
1024             allRelTypes.add(arType[1]);
1025         }
1026 
1027         String asMethods = "";
1028         for (String type : allRelTypes)
1029         {
1030             String lc = type.toLowerCase();
1031             asMethods += asJava.replaceAll("%Type%", type).replaceAll("%type%", lc);
1032         }
1033 
1034         File outPath = new File(absoluteRootPath + relativePath);
1035         outPath.mkdirs();
1036         PrintWriter out = new PrintWriter(absoluteRootPath + relativePath + "FloatSIMatrix.java");
1037         String java = siJava.replace("%%ASMETHODS%%", asMethods);
1038         out.print(java);
1039         out.close();
1040         System.out.println("built: " + absoluteRootPath + relativePath + "FloatSIMatrix.java");
1041     }
1042 
1043     /****************************************************************************************************************/
1044     /********************************************* GENERIC **********************************************************/
1045     /****************************************************************************************************************/
1046 
1047     /**
1048      * Determine calculated absolute output path (root of the executable or Jar file). In case of an Eclipse run, ../../ is
1049      * added to the path to place the results in the root of the project, rather than in target/classes.<br>
1050      * See https://weblogs.java.net/blog/kohsuke/archive/2007/04/how_to_convert.html and
1051      * http://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file and
1052      * http://stackoverflow.com/questions/3153337/get-current-working-directory-in-java
1053      * @throws FileNotFoundException in case file could not be found
1054      */
1055     private static void makeAndCleanAbsolutePath() throws FileNotFoundException
1056     {
1057         URL mainURL = URLResource.getResource("/");
1058         String path;
1059         try
1060         {
1061             path = mainURL.toURI().getPath();
1062         }
1063         catch (URISyntaxException exception)
1064         {
1065             path = mainURL.getPath();
1066         }
1067         if (path.endsWith("/target/classes/"))
1068         {
1069             path = path.substring(0, path.length() - "/target/classes/".length());
1070         }
1071         path += generatedCodeRelativePath;
1072         if (!new File(path).exists())
1073         {
1074             new File(path).mkdirs();
1075         }
1076         else
1077         {
1078             System.out.println("about to delete: " + path);
1079             // if (!deleteRecursive(new File(path)))
1080             // {
1081             // System.err.println("Could not empty directory " + path);
1082             // System.exit(-1);
1083             // }
1084         }
1085         absoluteRootPath = path;
1086         System.out.println("writing into: " + path);
1087     }
1088 
1089     /**
1090      * By default File#delete fails for non-empty directories, it works like "rm". We need something a little more brutal - this
1091      * does the equivalent of "rm -r". From: http://stackoverflow.com/questions/779519/delete-files-recursively-in-java. Note:
1092      * USE CAREFULLY.
1093      * @param path File; Root File Path
1094      * @return true iff the file and all sub files/directories have been removed
1095      * @throws FileNotFoundException on error (e.g., locked file)
1096      */
1097     public static boolean deleteRecursive(final File path) throws FileNotFoundException
1098     {
1099         if (!path.exists())
1100         {
1101             throw new FileNotFoundException(path.getAbsolutePath());
1102         }
1103         boolean ret = true;
1104         if (path.isDirectory())
1105         {
1106             for (File f : path.listFiles())
1107             {
1108                 ret = ret && deleteRecursive(f);
1109             }
1110         }
1111         return ret && path.delete();
1112     }
1113 
1114     /**
1115      * @param args String[]; args, should be blank
1116      * @throws IOException on I/O error
1117      * @throws URISyntaxException when file could not be found on the file system
1118      */
1119     public static void main(final String[] args) throws IOException, URISyntaxException
1120     {
1121         generationTime = Instant.now().toString();
1122 
1123         makeAndCleanAbsolutePath();
1124         readAbsRelTypes();
1125         readRelTypes();
1126         readFormulas();
1127         readReplace();
1128 
1129         generateDoubleScalarAbsRel();
1130         generateDoubleScalarRel();
1131         generateFloatScalarAbsRel();
1132         generateFloatScalarRel();
1133 
1134         generateDoubleVectorAbsRel();
1135         generateDoubleVectorRel();
1136         generateFloatVectorAbsRel();
1137         generateFloatVectorRel();
1138 
1139         generateDoubleMatrixAbsRel();
1140         generateDoubleMatrixRel();
1141         generateFloatMatrixAbsRel();
1142         generateFloatMatrixRel();
1143 
1144         generateSIScalar();
1145         generateFloatSIScalar();
1146 
1147         generateSIVector();
1148         generateFloatSIVector();
1149 
1150         generateSIMatrix();
1151         generateFloatSIMatrix();
1152     }
1153 
1154 }