diff options
author | Norbert Preining <norbert@preining.info> | 2024-01-19 03:00:44 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2024-01-19 03:00:44 +0000 |
commit | 849336321485cc972b028c23f8276fa6bdf4818e (patch) | |
tree | 43e99f8f941334e9d2dab5b65c67787e0daa1408 /support/latex-dependency-grapher | |
parent | c4dbe7c982d00abf62e9a8927ecdef11521773a8 (diff) |
CTAN sync 202401190300
Diffstat (limited to 'support/latex-dependency-grapher')
21 files changed, 1918 insertions, 0 deletions
diff --git a/support/latex-dependency-grapher/LICENSE.txt b/support/latex-dependency-grapher/LICENSE.txt new file mode 100644 index 0000000000..70b0d8f778 --- /dev/null +++ b/support/latex-dependency-grapher/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2024] [david roth, yanis weibel, alexander streit] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
\ No newline at end of file diff --git a/support/latex-dependency-grapher/NOTICE.txt b/support/latex-dependency-grapher/NOTICE.txt new file mode 100644 index 0000000000..c5a8563336 --- /dev/null +++ b/support/latex-dependency-grapher/NOTICE.txt @@ -0,0 +1,5 @@ +Apache Commons CLI +Copyright 2002-2023 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/).
\ No newline at end of file diff --git a/support/latex-dependency-grapher/README.md b/support/latex-dependency-grapher/README.md new file mode 100644 index 0000000000..d0246991b3 --- /dev/null +++ b/support/latex-dependency-grapher/README.md @@ -0,0 +1,253 @@ +# LaTeX Package dependency grapher +>We've created the LaTeX Package Dependency Grapher, a Java application under an open-source license, to address the challenges +of managing dependencies and complexity in LaTeX projects. The tool takes a file or folder as input, scans for dependencies (usepackage, RequirePackage, include, input), +and outputs unused packages along with a GraphViz graph illustrating the dependencies. + +## Requirements +- kpsewhich(should be included in most LaTeX distributions +- [GraphViz](https://graphviz.org) + - dot program +- Java 17 or newer + +## Usage + + +### Run the application +To run the application run the follwing command: + +```console +java -jar latex-package-dependency-grapher-1.0.jar -p <your-latex-file> +``` + +This will then generate a .tex and .pdf file containing a +list of the dependencies of your file and the topological embedding of those dependencies. + +### Compile .dot file manually +It can happen, that on some Operating Systems the pdf for the graph can not be generated +from the .dot file automatically from our program. If that is the case you can compile them manually as follows: + +```console +dot -Tpdf -o dependencyGraph.pdf dependencyGraph.dot +``` + +Now generate the dependencies pdf file: + +```console +pdflatex dependencies_<your-file-name> +``` + +### Help +With the following command the various options are shown and explained. + +```console +java -jar latex-package-dependency-grapher-1.0.jar -h +``` + +## Appendix +### Authors +David Roth, Yanis Weibel, Alexander Streit +### Tutor +Dr. Simon Kramer +### License +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [2024] [david roth, yanis weibel, alexander streit] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` diff --git a/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/Application.java b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/Application.java new file mode 100644 index 0000000000..ce0a3a4873 --- /dev/null +++ b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/Application.java @@ -0,0 +1,36 @@ +package ch.bfh.lpdg; + +import ch.bfh.lpdg.datastructure.DependencyType; +import org.apache.commons.cli.ParseException; + +import static java.util.Collections.emptyList; + +public class Application { + public static void main(String[] args) { + + var interactionHandler = InteractionHandler.getInstance(); + var graphHelper = GraphHelper.getInstance(); + + try { + interactionHandler.handleOptions(args); + } catch (ParseException pE) { + System.err.println("Handling parameters went wrong: " + pE.getMessage()); + } + + var files = interactionHandler.getFiles(); + for (var file:files) { + try { + var res = DependencyScanner.getInstance().findDependencies(file.getAbsolutePath(), DependencyType.FILE, "", emptyList(), interactionHandler.getDepth()); + interactionHandler.printDebugMessage("Dependencies for file: "+file.getName()); + interactionHandler.printDebugMessage(res.toString()); + var latexHelper = new LatexHelper(file, res); + latexHelper.findUnnecessaryDependencies(interactionHandler.hasOverwriteFlag(), interactionHandler.hasMinimizeFlag()); + graphHelper.createFileForDependency(res, "./"); + } + catch (Exception e) + { + System.out.println("Error on scanning the file: "+file.getAbsolutePath()+"\n"+e); + } + } + } +} diff --git a/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/DependencyScanner.java b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/DependencyScanner.java new file mode 100644 index 0000000000..fa635b9532 --- /dev/null +++ b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/DependencyScanner.java @@ -0,0 +1,193 @@ +package ch.bfh.lpdg; + +import ch.bfh.lpdg.datastructure.Dependency; +import ch.bfh.lpdg.datastructure.DependencyType; + +import java.io.*; +import java.nio.file.Paths; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static java.util.Collections.emptyList; + +/** + * This class is responsible for scanning dependencies for a file input. + */ +public class DependencyScanner { + + public static final String REGEX_DEPENDENCIES = "\\u005Cusepackage\\{(.*?)}|\\u005CRequirePackage\\{(.*?)}|\\u005Cinput\\{(.*?)}|\\u005Cinclude\\{(.*?)}"; + public final static DependencyScanner INSTANCE = new DependencyScanner(); + private String filePathWithoutFileName; + + private DependencyScanner() { + } + + public static DependencyScanner getInstance() { + return INSTANCE; + } + + /** + * This method can be called recursively, it loops through all dependencies of the given file. + * This is done via a regex pattern -> it scans for all types in the DependencyType.java class. + * It scans each line of the file individually and if the line matches one of the patterns in the regex the extractDependency() + * method is called and is then added to the list of dependencies. + * + * @param filePath is needed to know the initial location of the document that has to be scanned + * @param type describes what type the currently scanned document has + * @param source describes what type of dependency the currently scanned dependency has + * @param alreadyAddedDependencies is needed to avoid dependency loops + * @param scanDepth limits how deep the scan looks into the dependencies + * @return returns a Dependency containing a list of all its dependencies itself + */ + public Dependency findDependencies(final String filePath, final DependencyType type, final String source, final List<String> alreadyAddedDependencies, final int scanDepth) { + final String fileName = Paths.get(filePath).getFileName().toString(); + final String fileNameWithoutExt = this.resolveDependencyName(fileName); + filePathWithoutFileName = String.copyValueOf(filePath.toCharArray()); + filePathWithoutFileName = filePathWithoutFileName.replace(fileName, ""); + + final Dependency dependency = new Dependency(type, fileNameWithoutExt, source); + + if(scanDepth > 0) { + try { + final BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath))); + final Pattern pattern = Pattern.compile(REGEX_DEPENDENCIES); + + String line; + while ((line = br.readLine()) != null) { + final Matcher matcher = pattern.matcher(line); + + while (matcher.find()) { + final String usepackage = matcher.group(1); + final String requirepackage = matcher.group(2); + final String input = matcher.group(3); + final String include = matcher.group(4); + + // Create dependencies for dependency + final List<Dependency> extractedUP = this.extractDependencies(usepackage, DependencyType.USE_PACKAGE, matcher.group(0), alreadyAddedDependencies, scanDepth); + final List<Dependency> extractedRP = this.extractDependencies(requirepackage, DependencyType.REQUIRE_PACKAGE, matcher.group(0), alreadyAddedDependencies, scanDepth); + final List<Dependency> extractedINP = this.extractDependencies(input, DependencyType.INPUT, matcher.group(0), alreadyAddedDependencies, scanDepth); + final List<Dependency> extractedINC = this.extractDependencies(include, DependencyType.INCLUDE, matcher.group(0), alreadyAddedDependencies, scanDepth); + + // Add dependency to tree + final List<Dependency> extractedDependencies = Stream.of(extractedUP, extractedRP, extractedINP, extractedINC).filter(Objects::nonNull).flatMap(Collection::stream).toList(); + extractedDependencies.forEach(dep -> dependency.getDependencyList().add(dep)); + } + } + br.close(); + } catch (FileNotFoundException ex) { + InteractionHandler.getInstance().printDebugMessage("File: " + fileName + " was not found."); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + var cleanDependencies = removeInvalidDependencies(dependency); + dependency.setDependencyList(cleanDependencies); + + return dependency; + } + + /** + * This method extracts all dependencies for a Dependency scanned in the findDependencies() method. + * + * @return a list of dependencies, this is required for a format like requirePackage{array, color} + */ + private List<Dependency> extractDependencies(final String dependencyName, final DependencyType type, final String source, final List<String> alreadyAddedDependencies, final int scanDepth) { + final List<String> currentDependencies = new ArrayList<>(alreadyAddedDependencies); + if (dependencyName != null && !dependencyName.isEmpty()) { + // Resolve multiline usePackage dependencies, ex. usePackage{array,color} + if (dependencyName.contains(",")) { + final String[] packages = dependencyName.split(","); + final List<Dependency> foundDependencies = new ArrayList<>(); + Arrays.stream(packages).forEach(p -> { + if (!currentDependencies.contains(p)) { + // Try to resolve the path for the found dependency, if a path is found, the file has to be scanned recursively. + final String filePath = this.resolvePath(p, type); + if (filePath == null) { + foundDependencies.add(new Dependency(type, p, source)); + } else { + currentDependencies.add(p); + final Dependency dependency = this.findDependencies(filePath, type, source, currentDependencies, scanDepth-1); + if (dependency != null) { + foundDependencies.add(dependency); + } + } + } + }); + return foundDependencies; + } else { + if (!alreadyAddedDependencies.contains(dependencyName)) { + // Try to resolve the path for the found dependency, if a path is found, the file has to be scanned recursively. + final String filePath = this.resolvePath(dependencyName, type); + if (filePath == null) { + return Collections.singletonList(new Dependency(type, dependencyName, source)); + } else { + currentDependencies.add(dependencyName); + return List.of(this.findDependencies(filePath, type, source, currentDependencies, scanDepth -1)); + } + } + } + } + return emptyList(); + } + + /** + * This method resolves a path for a LaTeX dependency + * @param packageName the name of the package that should be searched for with kpsewhich + * @param type the type of the package, this is needed to search for project dependencies for input and include + * @return returns the path of the dependency location + */ + private String resolvePath(final String packageName, final DependencyType type) { + //Execute kpsewhich tool to find path of package + Process process; + try { + boolean hasExtension = packageName.lastIndexOf('.') != -1; + + //If the package doesn't have an extension add the standard one + process = Runtime.getRuntime().exec(String.format("kpsewhich %s%s", packageName, hasExtension ? "" : ".sty")); + } catch (IOException e) { + System.err.println("Couldn't execute kpsewhich command."); + throw new RuntimeException(e); + } + + StringBuilder result = new StringBuilder(); + BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); + String s; + while (true) { + try { + if ((s = stdInput.readLine()) == null) break; //Path not found + } catch (IOException e) { + System.err.println("Couldn't read output of kpsewhich command."); + throw new RuntimeException(e); + } + + result.append(s); + } + + if (result.isEmpty()) { + if (type.equals(DependencyType.INCLUDE) || type.equals(DependencyType.INPUT)) { + return filePathWithoutFileName + packageName + ".tex"; + } + return null; + } + + return Paths.get(result.toString()).toAbsolutePath().toString(); + } + + /** + * Removes invalid dependencies like variable names. + * Example: #1.def, \gin@driver + * + * @param d The List of all found dependencies + * @return A clean list without variable names and other invalid dependencies + */ + private List<Dependency> removeInvalidDependencies(Dependency d) { + return d.getDependencyList().stream().filter(e -> !e.getName().contains("#") && !e.getName().contains("@")).toList(); + } + + private String resolveDependencyName(final String fileName) { + return fileName.split("\\.")[0]; + } +} diff --git a/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/GraphHelper.java b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/GraphHelper.java new file mode 100644 index 0000000000..fd6a754b7d --- /dev/null +++ b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/GraphHelper.java @@ -0,0 +1,181 @@ +package ch.bfh.lpdg; + +import ch.bfh.lpdg.datastructure.Dependency; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public class GraphHelper { + + public final static GraphHelper INSTANCE = new GraphHelper(); + private final InteractionHandler interactionHandler = InteractionHandler.getInstance(); + + private GraphHelper() { + } + + public static GraphHelper getInstance() { + return INSTANCE; + } + + /** + * This method creates a new .tex file containing a list of the found dependencies and the dependeccy graph created with Graphviz + * + * @param dependency for which the .tex file should be created + * @param outputPath in which the created file should be located. + * @return the path of the newly created .tex file. + */ + public Path createFileForDependency(final Dependency dependency, final String outputPath) { + final String dependencyFileName = "dependencies_" + dependency.getName() + ".tex"; + final String dependencyFilePath = outputPath + dependencyFileName; + var dependencyFile = new File(outputPath + dependencyFileName); + + var latexHelper = new LatexHelper(); + this.writeDependencyToFile(dependencyFile, dependency); + + var result = latexHelper.compileTempDocument(outputPath, dependencyFilePath); + interactionHandler.printDebugMessage(String.valueOf(result)); + + if (!generatePdfFromDotFile(outputPath + "dependencyGraph.dot")) { + interactionHandler.printDebugMessage("Something went wrong trying to generate a pdf from the dot file."); + } + + //Second compile cycle to include the generated graph in the file. + var result2 = latexHelper.compileTempDocument(outputPath, dependencyFilePath); + interactionHandler.printDebugMessage(String.valueOf(result2)); + + return dependencyFile.toPath(); + } + + /** + * This method created the .tex file containing a list of the dependencies and its graph. + * + * @param file in which should be written + * @param dependency the dependency for which the graph should be created + */ + private void writeDependencyToFile(final File file, final Dependency dependency) { + try { + final String dependencyList = dependency.toLaTeXString(); + final String graphString = dependency.toGraphString(); + final String truncatedGraphString = this.truncateGraphString(graphString); + final String reorganizedGraphString = this.reorganizeGraphString(truncatedGraphString); + + BufferedWriter writer = Files.newBufferedWriter(file.toPath()); + writer.write(""" + \\documentclass{article} + \\usepackage[pdf]{graphviz} + \\usepackage{pdflscape} + \\begin{document} + \\begin{verbatim} + """); + writer.write(dependencyList); + writer.write("\\end{verbatim}\n"); + + if (dependency.getDependencyList().size() != 0) { + writer.write("\\pagebreak\n"); + writer.write("\\begin{landscape}\n"); + writer.write("\\digraph{dependencyGraph}{\n"); + writer.write("\tnewrank=true;\n\t"); + writer.write("\trank=\"same\";\n\t"); + writer.write("\trankdir=TB;\n\t"); + writer.write("\tratio=\"fill\";\n\t"); + writer.write("\tsize=\"8.3,11.7!\";\n\t"); + writer.write("\tmargin=0;\n\t"); + writer.write(reorganizedGraphString); + writer.write("}\n"); + writer.write("\\end{landscape}\n"); + } + + writer.write("\\end{document}\n"); + writer.close(); + } catch (IOException e) { + System.err.println("Error in the temporary file: " + e); + } + } + + /** + * Removes duplicated lines from graphString + * + * @param graphString for which duplicated lines should be removed + * @return a truncated string + */ + private String truncateGraphString(final String graphString) { + StringBuilder builder = new StringBuilder(); + for (String line : new LinkedHashSet<>(Arrays.asList(graphString.split("\n")))) { + builder.append(line).append("\n"); + } + return builder.toString(); + } + + private String reorganizeGraphString(String graphString) { + StringBuilder result = new StringBuilder(); + String[] lines = graphString.split("\n"); + + Map<String, Integer> lastOccurrences = new LinkedHashMap<>(); + + // Iterate to find the last occurrence index for each node + for (int i = 0; i < lines.length; i++) { + String line = lines[i].trim(); + if (!line.isEmpty()) { + String[] parts = line.split(" -> "); + String rightNode = parts[1].substring(0, parts[1].indexOf(';')).trim(); + lastOccurrences.put(rightNode, i); + } + } + + // Append lines with the last occurrences + for (int i : lastOccurrences.values()) { + result.append(lines[i]).append("\n"); + } + + // Append lines with other occurrences + for (int i = 0; i < lines.length; i++) { + String line = lines[i].trim(); + if (!line.isEmpty()) { + String[] parts = line.split(" -> "); + String rightNode = parts[1].substring(0, parts[1].indexOf(';')).trim(); + + // Skip if it's the last occurrence + if (i != lastOccurrences.get(rightNode)) { + result.append(lines[i]).append("\n"); + } + } + } + + return result.toString(); + } + + /** + * This method generated a pdf document for the .dot document. + * This is required to include it to the .tex document given to the user. + * + * @param dependencyGraphPath of the generated .dot file + * @return if the generation was successful or not + */ + private boolean generatePdfFromDotFile(final String dependencyGraphPath) { + try { + List<String> cmd = Arrays.asList( + "dot", + "-Tpdf", + "-o", dependencyGraphPath.replace("dot", "pdf"), + dependencyGraphPath); + + ProcessBuilder processBuilder = new ProcessBuilder(cmd); + + // Redirect the error stream to the output stream + processBuilder.redirectErrorStream(true); + + Process process = processBuilder.start(); + + var res = process.waitFor(); + + return res == 0; + } catch (Exception e) { + System.err.println("Error while compiling the dot file to pdf: " + e); + return false; + } + } +}
\ No newline at end of file diff --git a/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/InteractionHandler.java b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/InteractionHandler.java new file mode 100644 index 0000000000..9175364b82 --- /dev/null +++ b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/InteractionHandler.java @@ -0,0 +1,218 @@ +package ch.bfh.lpdg; + +import org.apache.commons.cli.*; + +import java.io.File; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class InteractionHandler { + + //Definition of all the possible options. Short with one letter, or longer with one word. + private static final String PathOption = "p"; + private static final String HelpOption = "h"; + private static final String DepthOption = "d"; + private static final String OverwriteOption = "o"; + private static final String MinimizeOption = "m"; + private static final String LicenseOptionLong = "license"; + private static final String DebugOptionLong = "debug"; + private boolean isSingleFile = false; + private boolean isDebug = false; + private boolean overwriteFlag = false; + private boolean minimizeFlag = false; + private final Options options; + private final String userDirectory; + private String path; + private int depth = 5; + + public boolean hasOverwriteFlag() { + return overwriteFlag; + } + + public boolean hasMinimizeFlag() { + return minimizeFlag; + } + + public String getPath() { + return this.path; + } + + public int getDepth() { + return this.depth; + } + + public void setPath(final String path) { + this.path = path; + } + + public String getUserDirectory() { + return userDirectory; + } + + public final static InteractionHandler INSTANCE = new InteractionHandler(); + + public static InteractionHandler getInstance() { + return INSTANCE; + } + + private InteractionHandler() { + //The default value is the current directory where the application was called from. + userDirectory = System.getProperty("user.dir"); + path = userDirectory; + + options = InitOptions(); + } + + private static Options InitOptions() { + Options options = new Options(); + + options.addOption(Option.builder(HelpOption) + .desc("Print this message and quit.") + .longOpt("help").build()); + + options.addOption(Option.builder(DepthOption) + .hasArg() + .argName("Depth") + .desc("The depth of the scan. Default value is 5.") + .longOpt("depth").build()); + + options.addOption(Option.builder(OverwriteOption) + .desc("Overwrite the input file(s), with a version that only includes the necessary packages.") + .longOpt("overwrite").build()); + + options.addOption(Option.builder(MinimizeOption) + .desc("Creates a minimized version of the input file(s), with only the necessary packages.") + .longOpt("minimize").build()); + + options.addOption(Option.builder(PathOption) + .desc("File or path to be scanned. The default value is the current working directory.") + .hasArg() + .argName("Path") + .longOpt("Path").build()); + + options.addOption(Option.builder() + .desc("Print the Licensing information and quit.") + .longOpt(LicenseOptionLong).build()); + + options.addOption(Option.builder() + .desc("Print debug information.") + .longOpt(DebugOptionLong).build()); + + return options; + } + + public void handleOptions(String[] args) throws ParseException { + CommandLineParser parser = new DefaultParser(); + CommandLine cmd = parser.parse(options, args); + + if (cmd.hasOption(DebugOptionLong)) { + this.isDebug = true; + printDebugMessage("Debugging."); + } + + if (cmd.hasOption(HelpOption)) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("java -jar LatexDependencyGrapher", "Scan your Latex file(s) to find unused pacakges and create a graph of your dependencies." , options, ""); + printDebugMessage("HelpOption called."); + System.exit(0); + } + + if (cmd.hasOption(PathOption)) { + path = cmd.getOptionValue(PathOption); + printDebugMessage("PathOption called. Values is: " + path); + if (path.isBlank() || path.isEmpty()) + throw new MissingArgumentException("The value for the path is missing."); + } + + if (cmd.hasOption(DepthOption)) { + depth = Integer.parseInt(cmd.getOptionValue(DepthOption)); + printDebugMessage("DepthOption called. Values is: " + depth); + } + + if (cmd.hasOption(LicenseOptionLong)) { + System.out.println("This project uses the Apache Commons CLI Library, which is licensed under Apache License 2.0. See more in the NOTICE.txt File."); + printDebugMessage("LicenseOption called. Values is: " + path); + System.exit(0); + } + + if (cmd.hasOption(MinimizeOption)) { + minimizeFlag = true; + printDebugMessage("MinimizeOption called."); + } + + if (cmd.hasOption(OverwriteOption)) { + overwriteFlag = true; + printDebugMessage("OverwriteOption called."); + } + + var validateResult = validatePath(path); + if (!validateResult) + throw new InvalidParameterException("The given path is invalid: " + path); + } + + public List<File> getFiles() { + List<File> files; + if (isSingleFile) { + if (path.endsWith(".sty") || path.endsWith(".cls") || path.endsWith(".tex")) { + files = new ArrayList<>() {{ + add(new File(path)); + }}; + } else + throw new IllegalArgumentException("The given Path is not a LaTeX-File."); + } else + files = loadLatexFiles(); + + return files; + } + + public void printDebugMessage(String msg) { + if (this.isDebug) + System.out.println("\u001B[33m" + "DEBUG: " + msg + "\u001B[0m"); + } + + private Boolean validatePath(String path) { + if (path == null) { + printDebugMessage("Path is null."); + return false; + } + File file = new File(path); + printDebugMessage("Filepath: " + file.getAbsolutePath()); + if (!file.isDirectory()) { + printDebugMessage("Path is not a directory"); + isSingleFile = file.exists(); + file = file.getParentFile(); + } else { + isSingleFile = false; + } + + if(isSingleFile) + { + printDebugMessage("The path is a single file."); + if(!(path.endsWith(".sty") || path.endsWith(".cls") || path.endsWith(".tex"))) + { + throw new IllegalArgumentException("The given Path is not a LaTeX-File."); + } + } + + return file != null && file.exists(); + } + + private List<File> loadLatexFiles() { + + if (isSingleFile) + throw new IllegalCallerException("This method should only be called to load the LaTeX-Files from a directory."); + + List<File> latexFiles = new ArrayList<>(); + File directory = new File(path); + if (directory.isDirectory()) { + File[] files = directory.listFiles((dir, name) -> name.endsWith(".sty") || name.endsWith(".cls") || name.endsWith(".tex")); + if (files != null) { + latexFiles.addAll(Arrays.asList(files)); + } + } + + return latexFiles; + } +} diff --git a/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/LatexHelper.java b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/LatexHelper.java new file mode 100644 index 0000000000..a77a0e6183 --- /dev/null +++ b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/LatexHelper.java @@ -0,0 +1,148 @@ +package ch.bfh.lpdg; + +import ch.bfh.lpdg.datastructure.Dependency; +import ch.bfh.lpdg.datastructure.DependencyType; +import lombok.NoArgsConstructor; +import org.springframework.util.FileSystemUtils; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@NoArgsConstructor +public class LatexHelper { + + private final InteractionHandler interactionHandler = InteractionHandler.getInstance(); + private Path tempFile; + private Path sourceFile; + private Path tempFolder; + private Dependency dependencies; + + public LatexHelper(File file, Dependency dependencies) throws IOException { + this.dependencies = dependencies; + this.tempFile = createTempFile(file, ".temp"); + this.sourceFile = file.toPath(); + this.tempFolder = createTempFolder(Paths.get(file.getParent())); + interactionHandler.printDebugMessage("Temp-File has been created. " + tempFile); + } + + private static String getFileExtension(String fileName) { + int lastDotIndex = fileName.lastIndexOf("."); + return (lastDotIndex != -1) ? fileName.substring(lastDotIndex) : ""; + } + + public void findUnnecessaryDependencies(Boolean overwrite, Boolean minimize) throws IOException { + this.interactionHandler.printDebugMessage("Finding unnecessary Dependencies."); + List<Dependency> unnecessaryDependencies = new ArrayList<>(); + for (var dep : dependencies.getDependencyList()) { + if(dep.getType() != DependencyType.REQUIRE_PACKAGE && dep.getType() != DependencyType.USE_PACKAGE) + continue; + this.interactionHandler.printDebugMessage("Trying without " + dep.toString()); + this.replaceDependency(tempFile, dep.getSource(), "%" + dep.getSource()); + if (this.compileTempDocument(tempFolder.toAbsolutePath().toString(), tempFile.toAbsolutePath().toString())) { + unnecessaryDependencies.add(dep); + dep.IsUnused = true; + } + this.replaceDependency(tempFile, "%" + dep.getSource(), dep.getSource()); + } + this.deleteTempFiles(); + + if(unnecessaryDependencies.size() > 0) { + System.out.println("\nThe following Packages are not needed:"); + for (var dep : unnecessaryDependencies) + System.out.println("\t" + dep.getName()); + } + + if (minimize || overwrite) { + Path fileToMinimize = overwrite ? this.sourceFile : createTempFile(sourceFile.toFile(), ".minimized"); + this.writeMinimizedFile(fileToMinimize); + } + } + + public boolean compileTempDocument(final String outputDirectory, final String documentToCompile) { + try { + List<String> cmd = Arrays.asList( + "pdflatex", //We use pdflatex to compile the documents. + "-output-directory", outputDirectory, //Set the output directory to the created tempFolder directory of the input + "-interaction", "nonstopmode", + "-halt-on-error", + "-file-line-error", + documentToCompile); + this.interactionHandler.printDebugMessage("Running: " + cmd); + ProcessBuilder processBuilder = new ProcessBuilder(cmd); + + // Redirect the error stream to the output stream + processBuilder.redirectErrorStream(true); + + Process process = processBuilder.start(); + InputStream is = process.getInputStream(); + int readBytes; + byte[] buffer = new byte[1024]; + StringBuilder outputBuilder = new StringBuilder(); + + while ((readBytes = is.read(buffer)) != -1) { + String chunk = new String(buffer, 0, readBytes, StandardCharsets.UTF_8); + outputBuilder.append(chunk); + } + this.interactionHandler.printDebugMessage(outputBuilder.toString()); + + var res = process.waitFor(); + this.interactionHandler.printDebugMessage("ReturnCode: " + res); + return res == 0; + } catch (Exception e) { + System.err.println("Error while compiling the temporary file: " + e); + return false; + } + } + + private void writeMinimizedFile(Path minimizedFile) { + for (var dep : dependencies.getDependencyList()) { + if (!dep.IsUnused) + continue; + replaceDependency(minimizedFile, dep.getSource(), "%" + dep.getSource()); + } + } + + private void replaceDependency(Path file, String dependency, String replaceWith) { + this.interactionHandler.printDebugMessage("Replacing " + dependency + " with " + replaceWith); + try { + BufferedReader reader = Files.newBufferedReader(file); + StringBuilder content = new StringBuilder(); + + String row; + while ((row = reader.readLine()) != null) { + content.append(row).append(System.lineSeparator()); + } + + String newContent = content.toString().replace(dependency, replaceWith); + + BufferedWriter writer = Files.newBufferedWriter(file); + writer.write(newContent); + + reader.close(); + writer.close(); + } catch (IOException e) { + System.err.println("Error in the temporary file: " + e); + } + } + + private Path createTempFile(File file, String identifier) throws IOException { + var extension = getFileExtension(file.getName()); + return Files.copy(Paths.get(file.getAbsolutePath()), Paths.get(file.getPath().replace(extension, identifier + extension)), StandardCopyOption.REPLACE_EXISTING); + } + + private Path createTempFolder(Path parentDirectory) throws IOException { + return Files.createTempDirectory(parentDirectory, "LatexDependencyTemp"); + } + + private void deleteTempFiles() throws IOException { + FileSystemUtils.deleteRecursively(tempFolder.toFile()); + Files.delete(tempFile); + } +} diff --git a/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/datastructure/Dependency.java b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/datastructure/Dependency.java new file mode 100644 index 0000000000..d27895462c --- /dev/null +++ b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/datastructure/Dependency.java @@ -0,0 +1,67 @@ +package ch.bfh.lpdg.datastructure; + +import lombok.Getter; +import lombok.Setter; + +import java.util.*; + +@Getter +@Setter +public class Dependency { + + public boolean IsUnused; + public String Source; + + private DependencyType type; + private String name; + private List<Dependency> dependencyList; + + public Dependency(DependencyType type, String name, String source) { + this.type = type; + this.name = name; + this.dependencyList = new ArrayList<>(); + this.Source = source; + } + + @Override + public String toString() { + return print(0); + } + + private String print(int numTabs) { + StringBuilder s = new StringBuilder(name + " (" + type.toString() + ")\n"); + + for (Dependency d : dependencyList) { + s.append("\t".repeat(numTabs + 1)).append(d.print(numTabs + 1)); + } + + return s.toString(); + } + + public String toLaTeXString() { + return appendLine(0); + } + + private String appendLine(int numTabs) { + StringBuilder s = new StringBuilder(name + " (" + type.toString() + ")\n"); + + for (Dependency d : dependencyList) { + s.append("\t".repeat(numTabs + 1)).append(d.appendLine(numTabs + 1)); + } + + return s.toString(); + } + + public String toGraphString() { + StringBuilder s = new StringBuilder(); + + for (Dependency d : dependencyList) { + var newStr = name.replace("\\", "").replace("@", "").replace(".", "").replace("-", "").replace("/", ""); + var newStrDep = d.name.replace("\\", "").replace("@", "").replace(".", "").replace("-", "").replace("/", ""); + s.append(newStr).append(" -> ").append(newStrDep).append(";\n"); + s.append(d.toGraphString()); + } + + return s.toString(); + } +} diff --git a/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/datastructure/DependencyType.java b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/datastructure/DependencyType.java new file mode 100644 index 0000000000..af93ddb941 --- /dev/null +++ b/support/latex-dependency-grapher/source/main/java/ch/bfh/lpdg/datastructure/DependencyType.java @@ -0,0 +1,9 @@ +package ch.bfh.lpdg.datastructure; + +public enum DependencyType { + FILE, + USE_PACKAGE, + REQUIRE_PACKAGE, + INCLUDE, + INPUT +} diff --git a/support/latex-dependency-grapher/source/pom.xml b/support/latex-dependency-grapher/source/pom.xml new file mode 100644 index 0000000000..ecc35e6c9a --- /dev/null +++ b/support/latex-dependency-grapher/source/pom.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>ch.bfh.project1.lpdg</groupId> + <artifactId>latex-package-dependency-grapher</artifactId> + <version>1.0</version> + <properties> + <maven.compiler.source>17</maven.compiler.source> + <maven.compiler.target>17</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <build> + <sourceDirectory>main/java</sourceDirectory> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>3.1.0</version> + <configuration> + <archive> + <manifest> + <mainClass>ch.bfh.lpdg.Application</mainClass> + </manifest> + </archive> + </configuration> + </plugin> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <archive> + <manifest> + <mainClass>ch.bfh.lpdg.Application</mainClass> + </manifest> + </archive> + <descriptorRefs> + <descriptorRef>jar-with-dependencies</descriptorRef> + </descriptorRefs> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>RELEASE</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-core</artifactId> + <version>4.3.10.RELEASE</version> + </dependency> + <!-- TEST dependencies--> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>3.24.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <version>5.10.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>commons-cli</groupId> + <artifactId>commons-cli</artifactId> + <version>1.6.0</version> + </dependency> + <dependency> + <groupId>org.javatuples</groupId> + <artifactId>javatuples</artifactId> + <version>1.2</version> + </dependency> + </dependencies> +</project> diff --git a/support/latex-dependency-grapher/source/test/java/DependencyScannerTest.java b/support/latex-dependency-grapher/source/test/java/DependencyScannerTest.java new file mode 100644 index 0000000000..d6bfcaaf7c --- /dev/null +++ b/support/latex-dependency-grapher/source/test/java/DependencyScannerTest.java @@ -0,0 +1,109 @@ +import ch.bfh.lpdg.DependencyScanner; +import ch.bfh.lpdg.InteractionHandler; +import ch.bfh.lpdg.datastructure.Dependency; +import ch.bfh.lpdg.datastructure.DependencyType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class DependencyScannerTest { + + DependencyScanner dependencyScanner = DependencyScanner.getInstance(); + private final String PATH = "src/test/resources/test.txt"; + private final String DIR_PATH = "/src/test/resources/"; + + @BeforeEach + public void setUp(){ + InteractionHandler instance = InteractionHandler.getInstance(); + instance.setPath(instance.getUserDirectory() + DIR_PATH); + } + + @Test + void findDependencies_checkDepenencyName() { + final File file = new File(PATH); + final String expectedDependencyName = "test"; + + var dependency = dependencyScanner.findDependencies(file.getAbsolutePath(), DependencyType.FILE, "", Collections.emptyList(), 5); + final String actualDependencyName = dependency.getName(); + + assertEquals(actualDependencyName, expectedDependencyName); + } + + @Test + void findDependencies_checkReturnValues() throws IOException { + final File file = new File(PATH); + + var dependency = dependencyScanner.findDependencies(file.getAbsolutePath(), DependencyType.FILE, "", Collections.emptyList(), 5); + // Using iterator().next() to get name out of Set<K> + var dependencies = dependency.getDependencyList(); + + final var usePackageDependencies = + resolveDependenciesForType(dependencies, DependencyType.USE_PACKAGE); + final var requirePackageDependencies = + resolveDependenciesForType(dependencies, DependencyType.REQUIRE_PACKAGE); + final var inputDependencies = + resolveDependenciesForType(dependencies, DependencyType.INPUT); + final var includeDependencies = + resolveDependenciesForType(dependencies, DependencyType.INCLUDE); + + assertThat(usePackageDependencies).containsAll(List.of("xcolor", "makecell", "rexim")); + assertThat(requirePackageDependencies).containsAll(List.of("testikus", "latexDev")); + assertThat(inputDependencies).containsAll(List.of("smallFile")); + assertThat(includeDependencies).containsAll(List.of("largeFile")); + + System.out.println(dependency); + } + + @Test + void test_ScanDepth() throws IOException { + final File file = new File(PATH); + + //If we scan with the depth of 0, no packages should be found. + var dependency = dependencyScanner.findDependencies(file.getAbsolutePath(), DependencyType.FILE, "", Collections.emptyList(), 0); + var dependencies = dependency.getDependencyList(); + assertTrue(dependencies.size() == 0); + + + //If we scan with the depth of 1, 7 packages should be found. + dependency = dependencyScanner.findDependencies(file.getAbsolutePath(), DependencyType.FILE, "", Collections.emptyList(), 1); + dependencies = dependency.getDependencyList(); + + assertTrue(dependencies.size() == 7); + //7 Packages, but these packages should not have been scanned again, since the depth was only 1 + boolean atLeastOneMoreFound = false; + for (Dependency p : dependencies) + { + if(p.getDependencyList().size() > 0) + atLeastOneMoreFound = true; + } + assertFalse(atLeastOneMoreFound); + + //If we scan with the depth of 2, we should again find 7 packages, but these packages should have dependencies themselves. + dependency = dependencyScanner.findDependencies(file.getAbsolutePath(), DependencyType.FILE, "", Collections.emptyList(), 2); + dependencies = dependency.getDependencyList(); + + assertTrue(dependencies.size() == 7); + //At least some packages should have dependencies themselves. + for (Dependency p : dependencies) + { + if(p.getDependencyList().size() > 0) + atLeastOneMoreFound = true; + } + assertTrue(atLeastOneMoreFound); + } + + private List<String> resolveDependenciesForType(final List<Dependency> dependencyList, final DependencyType type) { + return dependencyList.stream() + .filter(d -> d.getType().equals(type)).toList() + .stream() + .map(Dependency::getName).collect(toList()); + } +}
\ No newline at end of file diff --git a/support/latex-dependency-grapher/source/test/java/GraphHelperTest.java b/support/latex-dependency-grapher/source/test/java/GraphHelperTest.java new file mode 100644 index 0000000000..9b6cd44786 --- /dev/null +++ b/support/latex-dependency-grapher/source/test/java/GraphHelperTest.java @@ -0,0 +1,80 @@ +import ch.bfh.lpdg.DependencyScanner; +import ch.bfh.lpdg.GraphHelper; +import ch.bfh.lpdg.InteractionHandler; +import ch.bfh.lpdg.datastructure.DependencyType; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +class GraphHelperTest { + + final DependencyScanner dependencyScanner = DependencyScanner.getInstance(); + final GraphHelper graphHelper = new GraphHelper(); + + @Test + void findDependencies_checkDependencyName() throws IOException { + String PATH = "src/test/resources/LaTexFolder/file1.cls"; + final File file = new File(PATH); + + var dependency = dependencyScanner.findDependencies(file.getAbsolutePath(), DependencyType.FILE, "", Collections.emptyList(), 5); + + String outputPath = "src/test/resources/LaTexFolder/"; + var dependenciesFilePath = this.graphHelper.createFileForDependency(dependency, outputPath); + + final var resultFile = new File(dependenciesFilePath.toString()); + + final String dependenciesContent = new String(Files.readAllBytes(Paths.get(resultFile.toString()))); + final String graphContent = new String(Files.readAllBytes(Paths.get(outputPath + "dependencyGraph.dot"))); + + assertThat(resultFile.getName()).isEqualTo("dependencies_file1.tex"); + + // Check if dependencies file was created and contains correct dependency graph + assertThat(dependenciesContent).contains("\\digraph{dependencyGraph}{"); + assertThat(dependenciesContent).contains("file1 -> xcolor;"); + assertThat(dependenciesContent).contains("file1 -> makecell;"); + + // Check if graph file was created and contains correct dependencies + assertThat(graphContent).contains("digraph dependencyGraph "); + assertThat(graphContent).contains("file1 -> xcolor;"); + assertThat(graphContent).contains("file1 -> makecell;"); + } + + @Test + void findDependencies_check_include_abstract() throws IOException { + String PATH = "src/test/resources/LaTexFolder/file4.tex"; + String DIR_PATH = "/src/test/resources/LaTexFolder/"; + + InteractionHandler instance = InteractionHandler.getInstance(); + instance.setPath(instance.getUserDirectory() + DIR_PATH); + + final File file = new File(PATH); + + var dependency = dependencyScanner.findDependencies(file.getAbsolutePath(), DependencyType.FILE, "", Collections.emptyList(), 5); + + String outputPath = "src/test/resources/LaTexFolder/"; + var dependenciesFilePath = this.graphHelper.createFileForDependency(dependency, outputPath); + + final var resultFile = new File(dependenciesFilePath.toString()); + + final String dependenciesContent = new String(Files.readAllBytes(Paths.get(resultFile.toString()))); + final String graphContent = new String(Files.readAllBytes(Paths.get(outputPath + "dependencyGraph.dot"))); + + assertThat(resultFile.getName()).isEqualTo("dependencies_file4.tex"); + + // Check if dependencies file was created and contains correct dependency graph + assertThat(dependenciesContent).contains("\\digraph{dependencyGraph}{"); + assertThat(dependenciesContent).contains("abstract -> makecell;"); + assertThat(dependenciesContent).contains("makecell -> array;"); + + // Check if graph file was created and contains correct dependencies + assertThat(graphContent).contains("digraph dependencyGraph "); + assertThat(dependenciesContent).contains("abstract -> makecell;"); + assertThat(dependenciesContent).contains("makecell -> array;"); + } +}
\ No newline at end of file diff --git a/support/latex-dependency-grapher/source/test/java/InteractionHandlerTest.java b/support/latex-dependency-grapher/source/test/java/InteractionHandlerTest.java new file mode 100644 index 0000000000..2489c139b9 --- /dev/null +++ b/support/latex-dependency-grapher/source/test/java/InteractionHandlerTest.java @@ -0,0 +1,84 @@ +import ch.bfh.lpdg.InteractionHandler; +import org.apache.commons.cli.MissingArgumentException; +import org.apache.commons.cli.ParseException; +import org.junit.jupiter.api.Test; + +import java.security.InvalidParameterException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class InteractionHandlerTest { + @Test + public void testValidPathSingleFile() { + var handler1 = InteractionHandler.getInstance(); + var handler2 = InteractionHandler.getInstance(); + var handler3 = InteractionHandler.getInstance(); + try { + handler1.handleOptions(new String[]{"--debug", "-p", "src/test/resources/LaTexFolder/file1.cls"}); + handler2.handleOptions(new String[]{"--debug", "-p", "src/test/resources/LaTexFolder/file2.tex"}); + handler3.handleOptions(new String[]{"--debug", "-p", "src/test/resources/LaTexFolder/file3.sty"}); + } catch (Exception e) { + fail("This should have worked."); + } + } + + @Test + public void testValidPathSingleInvalidFile() { + var handler = InteractionHandler.getInstance(); + try { + handler.handleOptions(new String[]{"--debug", "-p", "src/test/resources/test.txt"}); + fail("The file is not a LaTeX-File and this should have failed."); + } catch (Exception e) { + assertEquals(IllegalArgumentException.class, e.getClass()); + } + } + + @Test + public void testValidPathMultipleFiles() throws ParseException { + var handler = InteractionHandler.getInstance(); + handler.handleOptions(new String[]{"--debug", "-p", "src/test/resources/LaTexFolder/"}); + + // Depending on thest order, there should be at least 3 scannable files. + assertThat(handler.getFiles().size()).isGreaterThan(3); + } + + @Test + public void testInvalidPath() { + var handler = InteractionHandler.getInstance(); + + try { + handler.handleOptions(new String[]{"--debug", "-p", "/TEST/INVALID"}); + } catch (Exception e) { + assertEquals(InvalidParameterException.class, e.getClass()); + assertEquals(e.getMessage(), "The given path is invalid: /TEST/INVALID"); + return; + } + fail("The path is invalid and should have thrown an exception."); + } + + @Test + public void testNullPath() { + var handler = InteractionHandler.getInstance(); + + try { + handler.handleOptions(new String[]{"--debug", "-p"}); + fail("The path is null and should have thrown an exception."); + } catch (Exception e) { + assertEquals(MissingArgumentException.class, e.getClass()); + } + } + + @Test + public void testEmptyPath() { + var handler = InteractionHandler.getInstance(); + + try { + handler.handleOptions(new String[]{"--debug", "-p", ""}); + fail("The path is empty and should have thrown an exception."); + } catch (Exception e) { + assertEquals(MissingArgumentException.class, e.getClass()); + } + } +} diff --git a/support/latex-dependency-grapher/source/test/java/LatexHelperTest.java b/support/latex-dependency-grapher/source/test/java/LatexHelperTest.java new file mode 100644 index 0000000000..13660f525b --- /dev/null +++ b/support/latex-dependency-grapher/source/test/java/LatexHelperTest.java @@ -0,0 +1,176 @@ +import ch.bfh.lpdg.DependencyScanner; +import ch.bfh.lpdg.LatexHelper; +import ch.bfh.lpdg.datastructure.DependencyType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class LatexHelperTest { + + private Path copyFile(File source) throws IOException { + int lastDotIndex = source.getName().lastIndexOf("."); + var extension = (lastDotIndex != -1) ? source.getName().substring(lastDotIndex) : ""; + return Files.copy(Paths.get(source.getAbsolutePath()), Paths.get(source.getPath().replace(extension, ".temp" + extension)), StandardCopyOption.REPLACE_EXISTING); + } + + private void removeFile(Path file) throws IOException { + Files.delete(file); + } + + private Boolean checkForFile(String filePath) { + File file = new File(filePath); + return file.exists() && file.isFile(); + } + + @AfterEach + public void cleanup() throws IOException { + removeFile(Paths.get("src/test/resources/LaTexFolder/file1.temp.cls")); + removeFile(Paths.get("src/test/resources/LaTexFolder/file2.temp.tex")); + removeFile(Paths.get("src/test/resources/LaTexFolder/file3.temp.sty")); + } + + @Test + public void testUnusedPackages() throws IOException { + var path1 = "src/test/resources/LaTexFolder/file1.cls"; + var copy1 = copyFile(new File(path1)); + var path2 = "src/test/resources/LaTexFolder/file2.tex"; + var copy2 = copyFile(new File(path2)); + var path3 = "src/test/resources/LaTexFolder/file3.sty"; + var copy3 = copyFile(new File(path3)); + + + var dep1 = DependencyScanner.getInstance().findDependencies(copy1.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + var dep2 = DependencyScanner.getInstance().findDependencies(copy2.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + var dep3 = DependencyScanner.getInstance().findDependencies(copy3.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + + var helper1 = new LatexHelper(copy1.toFile(), dep1); + var helper2 = new LatexHelper(copy2.toFile(), dep2); + var helper3 = new LatexHelper(copy3.toFile(), dep3); + + helper1.findUnnecessaryDependencies(false, false); + helper2.findUnnecessaryDependencies(false, false); + helper3.findUnnecessaryDependencies(false, false); + + var expectedUnusedDependencies = Arrays.asList("xcolor", "makecell"); + + for (var dep : dep1.getDependencyList()) { + if (expectedUnusedDependencies.contains(dep.getName())) + assertTrue(dep.IsUnused); + else + assertFalse(dep.isIsUnused()); + } + + for (var dep : dep2.getDependencyList()) { + if (expectedUnusedDependencies.contains(dep.getName())) + assertTrue(dep.IsUnused); + else + assertFalse(dep.isIsUnused()); + } + + for (var dep : dep3.getDependencyList()) { + if (expectedUnusedDependencies.contains(dep.getName())) + assertTrue(dep.IsUnused); + else + assertFalse(dep.isIsUnused()); + } + } + + @Test + public void testMinimize() throws IOException { + var path1 = "src/test/resources/LaTexFolder/file1.cls"; + var copy1 = copyFile(new File(path1)); + var path2 = "src/test/resources/LaTexFolder/file2.tex"; + var copy2 = copyFile(new File(path2)); + var path3 = "src/test/resources/LaTexFolder/file3.sty"; + var copy3 = copyFile(new File(path3)); + + var dep1 = DependencyScanner.getInstance().findDependencies(copy1.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + var dep2 = DependencyScanner.getInstance().findDependencies(copy2.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + var dep3 = DependencyScanner.getInstance().findDependencies(copy3.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + + var helper1 = new LatexHelper(copy1.toFile(), dep1); + var helper2 = new LatexHelper(copy2.toFile(), dep2); + var helper3 = new LatexHelper(copy3.toFile(), dep3); + + helper1.findUnnecessaryDependencies(false, true); + helper2.findUnnecessaryDependencies(false, true); + helper3.findUnnecessaryDependencies(false, true); + + assertTrue(checkForFile("src/test/resources/LaTexFolder/file1.temp.minimized.cls")); + assertTrue(checkForFile("src/test/resources/LaTexFolder/file2.temp.minimized.tex")); + assertTrue(checkForFile("src/test/resources/LaTexFolder/file3.temp.minimized.sty")); + + removeFile(Paths.get("src/test/resources/LaTexFolder/file1.temp.minimized.cls")); + removeFile(Paths.get("src/test/resources/LaTexFolder/file2.temp.minimized.tex")); + removeFile(Paths.get("src/test/resources/LaTexFolder/file3.temp.minimized.sty")); + } + + @Test + public void testOverwrite() throws IOException { + var path1 = "src/test/resources/LaTexFolder/file1.cls"; + var copy1 = copyFile(new File(path1)); + var path2 = "src/test/resources/LaTexFolder/file2.tex"; + var copy2 = copyFile(new File(path2)); + var path3 = "src/test/resources/LaTexFolder/file3.sty"; + var copy3 = copyFile(new File(path3)); + + var dep1 = DependencyScanner.getInstance().findDependencies(copy1.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + var dep2 = DependencyScanner.getInstance().findDependencies(copy2.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + var dep3 = DependencyScanner.getInstance().findDependencies(copy3.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + + var helper1 = new LatexHelper(copy1.toFile(), dep1); + var helper2 = new LatexHelper(copy2.toFile(), dep2); + var helper3 = new LatexHelper(copy3.toFile(), dep3); + + helper1.findUnnecessaryDependencies(true, false); + helper2.findUnnecessaryDependencies(true, false); + helper3.findUnnecessaryDependencies(true, false); + + assertFalse(checkForFile("src/test/resources/LaTexFolder/file1.temp.minimized.cls")); + assertFalse(checkForFile("src/test/resources/LaTexFolder/file2.temp.minimized.tex")); + assertFalse(checkForFile("src/test/resources/LaTexFolder/file3.temp.minimized.sty")); + assertTrue(checkForFile("src/test/resources/LaTexFolder/file1.temp.cls")); + assertTrue(checkForFile("src/test/resources/LaTexFolder/file2.temp.tex")); + assertTrue(checkForFile("src/test/resources/LaTexFolder/file3.temp.sty")); + } + + @Test + public void testMinimizeAndOverwrite() throws IOException { + var path1 = "src/test/resources/LaTexFolder/file1.cls"; + var copy1 = copyFile(new File(path1)); + var path2 = "src/test/resources/LaTexFolder/file2.tex"; + var copy2 = copyFile(new File(path2)); + var path3 = "src/test/resources/LaTexFolder/file3.sty"; + var copy3 = copyFile(new File(path3)); + + var dep1 = DependencyScanner.getInstance().findDependencies(copy1.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + var dep2 = DependencyScanner.getInstance().findDependencies(copy2.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + var dep3 = DependencyScanner.getInstance().findDependencies(copy3.toAbsolutePath().toString(), DependencyType.FILE, "", Collections.emptyList(), 5); + + var helper1 = new LatexHelper(copy1.toFile(), dep1); + var helper2 = new LatexHelper(copy2.toFile(), dep2); + var helper3 = new LatexHelper(copy3.toFile(), dep3); + + helper1.findUnnecessaryDependencies(true, true); + helper2.findUnnecessaryDependencies(true, true); + helper3.findUnnecessaryDependencies(true, true); + + assertFalse(checkForFile("src/test/resources/LaTexFolder/file1.temp.minimized.cls")); + assertFalse(checkForFile("src/test/resources/LaTexFolder/file2.temp.minimized.tex")); + assertFalse(checkForFile("src/test/resources/LaTexFolder/file3.temp.minimized.sty")); + assertTrue(checkForFile("src/test/resources/LaTexFolder/file1.temp.cls")); + assertTrue(checkForFile("src/test/resources/LaTexFolder/file2.temp.tex")); + assertTrue(checkForFile("src/test/resources/LaTexFolder/file3.temp.sty")); + } +} diff --git a/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file1.cls b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file1.cls new file mode 100644 index 0000000000..1dc4e640e5 --- /dev/null +++ b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file1.cls @@ -0,0 +1,15 @@ +\documentclass{article} + +\usepackage{xcolor} +\usepackage{makecell} +\include{includes/abstract} + + + +\begin{document} + + + +blabla + +\end{document}
\ No newline at end of file diff --git a/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file2.tex b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file2.tex new file mode 100644 index 0000000000..1dc4e640e5 --- /dev/null +++ b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file2.tex @@ -0,0 +1,15 @@ +\documentclass{article} + +\usepackage{xcolor} +\usepackage{makecell} +\include{includes/abstract} + + + +\begin{document} + + + +blabla + +\end{document}
\ No newline at end of file diff --git a/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file3.sty b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file3.sty new file mode 100644 index 0000000000..1dc4e640e5 --- /dev/null +++ b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file3.sty @@ -0,0 +1,15 @@ +\documentclass{article} + +\usepackage{xcolor} +\usepackage{makecell} +\include{includes/abstract} + + + +\begin{document} + + + +blabla + +\end{document}
\ No newline at end of file diff --git a/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file4.tex b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file4.tex new file mode 100644 index 0000000000..4e655c7f7d --- /dev/null +++ b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/file4.tex @@ -0,0 +1,7 @@ +\documentclass{article} + +\begin{document} + + \include{includes/abstract} + +\end{document}
\ No newline at end of file diff --git a/support/latex-dependency-grapher/source/test/resources/LaTexFolder/includes/abstract.tex b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/includes/abstract.tex new file mode 100644 index 0000000000..0b1021ea52 --- /dev/null +++ b/support/latex-dependency-grapher/source/test/resources/LaTexFolder/includes/abstract.tex @@ -0,0 +1,7 @@ +\usepackage{makecell} +\thispagestyle{plain} +\begin{center} + \Large + \textbf{Abstract} +\end{center} +This is the abstract of this document. Abstract has to be completed and is a brief introduction about the main subject.
\ No newline at end of file diff --git a/support/latex-dependency-grapher/source/test/resources/test.txt b/support/latex-dependency-grapher/source/test/resources/test.txt new file mode 100644 index 0000000000..8d6038cb08 --- /dev/null +++ b/support/latex-dependency-grapher/source/test/resources/test.txt @@ -0,0 +1,16 @@ +\documentclass{article} + +\usepackage{xcolor}\usepackage{makecell} +\RequirePackage{testikus} +\RequirePackage{latexDev} + +\usepackage{rexim} + +\begin{document} + +\input{smallFile} +\include{largeFile} + +blabla + +\end{document}
\ No newline at end of file |