|
| 1 | +package com.github.markusbernhardt.xmldoclet; |
| 2 | + |
| 3 | +import com.github.markusbernhardt.xmldoclet.xjc.Class; |
| 4 | +import com.github.markusbernhardt.xmldoclet.xjc.Package; |
| 5 | +import com.github.markusbernhardt.xmldoclet.xjc.Root; |
| 6 | +import org.junit.Test; |
| 7 | + |
| 8 | +import java.io.File; |
| 9 | +import java.io.PrintWriter; |
| 10 | +import java.nio.charset.Charset; |
| 11 | +import java.util.ArrayList; |
| 12 | +import java.util.Arrays; |
| 13 | +import java.util.List; |
| 14 | +import java.util.logging.Level; |
| 15 | +import java.util.logging.Logger; |
| 16 | +import java.util.spi.ToolProvider; |
| 17 | + |
| 18 | +import static java.util.Arrays.stream; |
| 19 | + |
| 20 | +/** |
| 21 | + * Base class for all tests. |
| 22 | + * |
| 23 | + * @author markus |
| 24 | + */ |
| 25 | +abstract class AbstractTest { |
| 26 | + private final static Logger LOGGER = Logger.getLogger(AbstractTest.class.getName()); |
| 27 | + |
| 28 | + protected static final String[] TEST_DIR = { "./src/test/java/" }; |
| 29 | + protected static final String SIMPLE_DATA_PACKAGE = "com.github.markusbernhardt.xmldoclet.simpledata"; |
| 30 | + protected static final String SIMPLE_DATA_DIR = TEST_DIR[0] + SIMPLE_DATA_PACKAGE.replaceAll("\\.", "/"); |
| 31 | + |
| 32 | + protected static final String[] ARGS = { "-dryrun" }; |
| 33 | + protected static final String[] SUB_PACKAGES = { "com" }; |
| 34 | + |
| 35 | + /** |
| 36 | + * Gets the first {@link Package} and {@link Class} from a JavaDoc {@link Root} element. |
| 37 | + * @param sourceFileName Name of a source files inside the {@link #SIMPLE_DATA_DIR} to test. |
| 38 | + */ |
| 39 | + public JavaDocElements newJavaDocElements(final String ...sourceFileName) { |
| 40 | + final var sourceFiles = stream(sourceFileName).map(this::getFilePathFromSimpleDataDir).toArray(String[]::new); |
| 41 | + final var rootNode = executeJavadoc(null, null, null, sourceFiles, null, ARGS); |
| 42 | + |
| 43 | + final var packageNode = rootNode.getPackage().getFirst(); |
| 44 | + final var classNode = packageNode.getClazz().getFirst(); |
| 45 | + return new JavaDocElements(rootNode, packageNode, classNode); |
| 46 | + } |
| 47 | + |
| 48 | + /** |
| 49 | + * Rigourous Parser :-) |
| 50 | + */ |
| 51 | + @Test |
| 52 | + public void testSampledoc() { |
| 53 | + executeJavadoc(".", TEST_DIR, null, null, SUB_PACKAGES, ARGS); |
| 54 | + } |
| 55 | + |
| 56 | + /** |
| 57 | + * Processes the source code using javadoc. |
| 58 | + * |
| 59 | + * @param extendedClassPath Any classpath information required to help along javadoc. Javadoc |
| 60 | + * will actually compile the source code you specify; so if there are any jars or classes |
| 61 | + * that are referenced by the source code to process, then including those compiled items |
| 62 | + * in the classpath will give you more complete data in the resulting XML. |
| 63 | + * @param sourcePaths Usually sourcePaths is specified in conjunction with either/both packages & |
| 64 | + * subpackages. The sourcepaths value should be the path of the source files right before |
| 65 | + * the standard package-based folder layout of projects begins. For example, if you have |
| 66 | + * code that exists in package foo.bar, and your code is physically in /MyFolder/foo/bar/, |
| 67 | + * then the sourcePaths would be /MyFolder |
| 68 | + * @param packages Use if you want to detail specific packages to process (contrast with |
| 69 | + * subpackages, which is probably the easiest/most brute force way of using xml-doclet). |
| 70 | + * If you have within your code two packages, foo.bar and bar.foo, but only wanted |
| 71 | + * foo.bar processed, then specify just 'foo.bar' for this argument. |
| 72 | + * @param sourceFiles You can specify source files individually. This usually is used instead of |
| 73 | + * sourcePaths/subPackages/packages. If you use this parameter, specify the full path of |
| 74 | + * any java file you want processed. |
| 75 | + * @param subPackages You can specify 'subPackages', which simply gives one an easy way to |
| 76 | + * specify the root package, and have javadoc recursively look through everything under |
| 77 | + * that package. So for instance, if you had foo.bar, foo.bar.bar, and bar.foo, |
| 78 | + * specifying 'foo' will process foo.bar and foo.bar.bar packages, but not bar.foo |
| 79 | + * (unless you specify 'bar' as a subpackage, too) |
| 80 | + * @param additionalArguments Additional Arguments. |
| 81 | + * @return XStream compatible data structure |
| 82 | + */ |
| 83 | + public Root executeJavadoc( |
| 84 | + final String extendedClassPath, final String[] sourcePaths, final String[] packages, |
| 85 | + final String[] sourceFiles, final String[] subPackages, final String[] additionalArguments) { |
| 86 | + try { |
| 87 | + |
| 88 | + final var errorWriter = new PrintWriter(System.err, true, Charset.defaultCharset()); |
| 89 | + final var infoWriter = new PrintWriter(System.out, true, Charset.defaultCharset()); |
| 90 | + |
| 91 | + // aggregate arguments and packages |
| 92 | + final var argumentList = new ArrayList<String>(); |
| 93 | + |
| 94 | + // by setting this to 'private', nothing is omitted in the parsing |
| 95 | + argumentList.add("-private"); |
| 96 | + |
| 97 | + final String classPath = getClassPath(extendedClassPath); |
| 98 | + argumentList.add("-classpath"); |
| 99 | + argumentList.add(classPath); |
| 100 | + |
| 101 | + if (sourcePaths != null) { |
| 102 | + final String concatedSourcePaths = join(File.pathSeparator, sourcePaths); |
| 103 | + if (!concatedSourcePaths.isEmpty()) { |
| 104 | + argumentList.add("-sourcepath"); |
| 105 | + argumentList.add(concatedSourcePaths); |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + if (subPackages != null) { |
| 110 | + final String concatedSubPackages = join(";", subPackages); |
| 111 | + if (!concatedSubPackages.isEmpty()) { |
| 112 | + argumentList.add("-subpackages"); |
| 113 | + argumentList.add(concatedSubPackages); |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + if (packages != null) { |
| 118 | + argumentList.addAll(Arrays.asList(packages)); |
| 119 | + } |
| 120 | + |
| 121 | + if (sourceFiles != null) { |
| 122 | + argumentList.addAll(Arrays.asList(sourceFiles)); |
| 123 | + } |
| 124 | + |
| 125 | + if (additionalArguments != null) { |
| 126 | + argumentList.addAll(Arrays.asList(additionalArguments)); |
| 127 | + } |
| 128 | + |
| 129 | + LOGGER.info("Executing doclet with arguments: " + join(" ", argumentList)); |
| 130 | + |
| 131 | + final var javadoc = ToolProvider.findFirst("javadoc").orElseThrow(); |
| 132 | + final String[] arguments = argumentList.toArray(new String[] {}); |
| 133 | + javadoc.run(infoWriter, errorWriter, arguments); |
| 134 | + |
| 135 | + LOGGER.info("done with doclet processing"); |
| 136 | + } catch (Exception e) { |
| 137 | + LOGGER.log(Level.SEVERE, "doclet exception", e); |
| 138 | + } catch (Error e) { |
| 139 | + LOGGER.log(Level.SEVERE, "doclet error", e); |
| 140 | + } |
| 141 | + |
| 142 | + return XmlDoclet.getRoot(); |
| 143 | + } |
| 144 | + |
| 145 | + private static String getClassPath(final String extendedClassPath) { |
| 146 | + final String classPath = System.getProperty("java.class.path", "."); |
| 147 | + return extendedClassPath == null ? classPath : classPath + File.pathSeparator + extendedClassPath; |
| 148 | + |
| 149 | + } |
| 150 | + |
| 151 | + /** |
| 152 | + * Helper method to concat strings. |
| 153 | + * |
| 154 | + * @param glue the separator. |
| 155 | + * @param strings the strings to concat. |
| 156 | + * @return concatenated string |
| 157 | + */ |
| 158 | + public static String join(final String glue, final String[] strings) { |
| 159 | + return join(glue, Arrays.asList(strings)); |
| 160 | + } |
| 161 | + |
| 162 | + /** |
| 163 | + * Helper method to concat strings. |
| 164 | + * |
| 165 | + * @param glue the separator. |
| 166 | + * @param strings the strings to concat. |
| 167 | + * @return concatenated string |
| 168 | + */ |
| 169 | + public static String join(final String glue, final List<String> strings) { |
| 170 | + if (strings == null) { |
| 171 | + return null; |
| 172 | + } |
| 173 | + |
| 174 | + return String.join(glue, strings); |
| 175 | + } |
| 176 | + |
| 177 | + |
| 178 | + /** |
| 179 | + * {@return the path (separated by /) to a source file in the {@link #SIMPLE_DATA_DIR}} |
| 180 | + * @param sourceFileName the name of the file to get its full path from the {@link #SIMPLE_DATA_DIR} |
| 181 | + */ |
| 182 | + protected String getFilePathFromSimpleDataDir(final String sourceFileName) { |
| 183 | + return join("/", List.of(SIMPLE_DATA_DIR, sourceFileName)); |
| 184 | + } |
| 185 | + |
| 186 | + /** |
| 187 | + * {@return the full qualified name of a class, interface, record, enum or even some other element inside |
| 188 | + * those ones (separated by .) in the {@link #SIMPLE_DATA_PACKAGE}} |
| 189 | + * Those internal elements can be something such as fields, methods, constructors, etc. |
| 190 | + * @param elementName the name of the element (class, interface, record, method, etc.) to get its |
| 191 | + * full qualified name from the {@link #SIMPLE_DATA_PACKAGE} |
| 192 | + */ |
| 193 | + protected static String getElementPathFromSimpleDataPackage(final String elementName) { |
| 194 | + return join(".", List.of(SIMPLE_DATA_PACKAGE, elementName)); |
| 195 | + } |
| 196 | + |
| 197 | +} |
0 commit comments