opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultCode.java
@@ -196,9 +196,6 @@ private static final Map<Integer, ResultCode> ELEMENTS = new LinkedHashMap<>(); private static final List<ResultCode> IMMUTABLE_ELEMENTS = Collections.unmodifiableList(new ArrayList<ResultCode>( ELEMENTS.values())); /** * The result code that should only be used if the actual result code has * not yet been determined. @@ -867,6 +864,9 @@ return result; } private static final List<ResultCode> IMMUTABLE_ELEMENTS = Collections.unmodifiableList(new ArrayList<ResultCode>( ELEMENTS.values())); /** * Returns an unmodifiable list containing the set of available result codes * indexed on their integer value as defined in RFC 4511 section 4.1.9. opendj-doc-maven-plugin/pom.xml
@@ -66,6 +66,11 @@ <version>2.2.0</version> </dependency> <dependency> <groupId>com.thoughtworks.qdox</groupId> <artifactId>qdox</artifactId> <version>2.0-M3</version> </dependency> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> <version>3.2</version> opendj-doc-maven-plugin/src/main/java/org/forgerock/opendj/maven/doc/GenerateResultCodeDocMojo.java
New file @@ -0,0 +1,184 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2015 ForgeRock AS. */ package org.forgerock.opendj.maven.doc; import static org.forgerock.opendj.maven.doc.Utils.*; import com.thoughtworks.qdox.JavaProjectBuilder; import com.thoughtworks.qdox.model.JavaClass; import com.thoughtworks.qdox.model.JavaField; import com.thoughtworks.qdox.model.JavaType; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.forgerock.opendj.ldap.ResultCode; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Generates documentation source for LDAP result codes based on * {@code org.forgerock.opendj.ldap.ResultCode}. * <br> * This implementation parses the source to match Javadoc comments with result codes. * It is assumed that the class's ResultCode fields are named with result code enum values, * and that those fields have Javadoc comments describing each result code. */ @Mojo(name = "generate-result-code-doc", defaultPhase = LifecyclePhase.COMPILE) public class GenerateResultCodeDocMojo extends AbstractMojo { /** * The Java file containing the source of the ResultCode class, * {@code org.forgerock.opendj.ldap.ResultCode}. * <br> * For example, {@code opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultCode.java}. */ @Parameter(required = true) private File resultCodeSource; /** The XML file to generate. */ @Parameter(required = true) private File xmlFile; /** * Generates documentation source for LDAP result codes. * * @throws MojoExecutionException Generation failed * @throws MojoFailureException Not used */ @Override public void execute() throws MojoExecutionException, MojoFailureException { final Map<String, Object> map = new HashMap<>(); map.put("year", new SimpleDateFormat("yyyy").format(new Date())); // The overall explanation in the generated doc is the class comment. final JavaClass resultCodeClass; try { resultCodeClass = getJavaClass(); } catch (IOException e) { throw new MojoExecutionException("Could not read " + resultCodeSource.getPath(), e); } map.put("classComment", cleanComment(resultCodeClass.getComment())); // Documentation for each result code comes from the Javadoc for the code, // and from the value and friendly name of the code. final Map<String, Object> comments = new HashMap<>(); for (final JavaField field : resultCodeClass.getFields()) { final JavaType type = field.getType(); if (type.getValue().equals("ResultCode")) { comments.put(field.getName(), cleanComment(field.getComment())); } } map.put("resultCodes", getResultCodesDoc(comments)); final String template = "appendix-ldap-result-codes.ftl"; try { writeStringToFile(applyTemplate(template, map), xmlFile); } catch (IOException e) { throw new MojoExecutionException("Could not write to " + xmlFile.getPath(), e); } getLog().info("Wrote " + xmlFile.getPath()); } /** * Returns an object to access to the result code Java source. * @return An object to access to the result code Java source. * @throws IOException Could not read the source */ private JavaClass getJavaClass() throws IOException { final JavaProjectBuilder builder = new JavaProjectBuilder(); builder.addSource(resultCodeSource); return builder.getClassByName("org.forgerock.opendj.ldap.ResultCode"); } /** * Returns a clean string for use in generated documentation. * @param comment The comment to clean. * @return A clean string for use in generated documentation. */ private String cleanComment(String comment) { return stripCodeValueSentences(stripTags(convertLineSeparators(comment))).trim(); } /** * Returns a string with line separators converted to spaces. * @param string The string to convert. * @return A string with line separators converted to spaces. */ private String convertLineSeparators(String string) { return string.replaceAll(System.lineSeparator(), " "); } /** * Returns a string with the HTML tags removed. * @param string The string to strip. * @return A string with the HTML tags removed. */ private String stripTags(String string) { return string.replaceAll("<[^>]*>", ""); } /** * Returns a string with lines sentences of the following form removed: * This result code corresponds to the LDAP result code value of {@code 0}. * @param string The string to strip. * @return A string with lines sentences of the matching form removed. */ private String stripCodeValueSentences(String string) { return string .replaceAll("This result code corresponds to the LDAP result code value of \\{@code \\d+\\}.", ""); } /** * Returns a list of documentation objects for all result codes. * @param comments A map of field names to the clean comments. * @return A list of documentation objects for all result codes. */ private List<Map<String, Object>> getResultCodesDoc(Map<String, Object> comments) { final List<Map<String, Object>> list = new LinkedList<>(); if (comments == null || comments.isEmpty()) { return list; } for (ResultCode resultCode : ResultCode.values()) { final Map<String, Object> doc = new HashMap<>(); doc.put("intValue", resultCode.intValue()); doc.put("name", resultCode.getName()); final Object comment = comments.get(resultCode.asEnum().toString()); doc.put("comment", comment); list.add(doc); } return list; } } opendj-doc-maven-plugin/src/main/java/org/forgerock/opendj/maven/doc/GenerateSchemaDocMojo.java
@@ -25,10 +25,8 @@ package org.forgerock.opendj.maven.doc; import static com.forgerock.opendj.ldap.CoreMessages.*; import static org.forgerock.opendj.maven.doc.Utils.*; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -36,13 +34,8 @@ import org.apache.maven.plugins.annotations.Parameter; import org.forgerock.opendj.ldap.schema.CoreSchemaSupportedLocales; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; @@ -77,12 +70,9 @@ final String localeReference = getLocalesAndSubTypesDocumentation(currentLocale); final File localeReferenceFile = new File(outputDirectory, "sec-locales-subtypes.xml"); try { createOutputDirectory(); writeStringToFile(localeReference, localeReferenceFile); } catch (FileNotFoundException e) { throw new MojoFailureException("Failed to write doc reference file.", e); } catch (IOException e) { throw new MojoExecutionException("Failed to create output directory"); throw new MojoExecutionException("Failed to write " + localeReferenceFile.getPath()); } } @@ -102,74 +92,9 @@ return applyTemplate("sec-locales-subtypes.ftl", map); } /** * Create the output directory if it does not exist. * @throws IOException Failed to create the directory. */ private void createOutputDirectory() throws IOException { if (outputDirectory != null && !outputDirectory.exists()) { if (!outputDirectory.mkdirs()) { throw new IOException("Failed to create output directory."); } } } /** * Writes a string to a file. * @param string The string to write. * @param file The file to write to. * @throws FileNotFoundException The file did not exist, or could not be created for writing. */ private void writeStringToFile(final String string, final File file) throws FileNotFoundException { PrintWriter printWriter = new PrintWriter(file); printWriter.print(string); printWriter.close(); } private final Map<String, String> localeTagsToOids = CoreSchemaSupportedLocales.getJvmSupportedLocaleNamesToOids(); /** FreeMarker template configuration. */ private Configuration configuration; /** * Returns a FreeMarker configuration for applying templates. * @return A FreeMarker configuration for applying templates. */ private Configuration getConfiguration() { if (configuration == null) { configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); configuration.setClassForTemplateLoading(GenerateSchemaDocMojo.class, "/templates"); configuration.setDefaultEncoding("UTF-8"); configuration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER); } return configuration; } /** * Returns the String result from applying a FreeMarker template. * @param template The name of a template file found in {@code resources/templates/}. * @param map The map holding the data to use in the template. * @return The String result from applying a FreeMarker template. */ private String applyTemplate(final String template, final Map<String, Object> map) { // FreeMarker requires a configuration to find the template. configuration = getConfiguration(); // FreeMarker takes the data and a Writer to process the template. ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Writer writer = new OutputStreamWriter(outputStream); try { Template configurationTemplate = configuration.getTemplate(template); configurationTemplate.process(map, writer); return outputStream.toString(); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { org.forgerock.util.Utils.closeSilently(writer, outputStream); } } /** Container for documentation regarding a locale. */ private class LocaleDoc { String tag; opendj-doc-maven-plugin/src/main/java/org/forgerock/opendj/maven/doc/Utils.java
@@ -27,16 +27,23 @@ import static org.forgerock.util.Utils.*; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.Writer; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; @@ -44,6 +51,7 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** @@ -96,9 +104,7 @@ if (original == null) { throw new IOException("Could not read input to copy."); } if (!copy.exists() && !copy.createNewFile()) { throw new IOException("Failed to create " + copy); } createFile(copy); OutputStream outputStream = new FileOutputStream(copy); int bytesRead; byte[] buffer = new byte[4096]; @@ -108,6 +114,32 @@ closeSilently(original, outputStream); } /** * Writes a string to a file. * @param string The string to write * @param file The file to write to * @throws IOException The file did not exist, or could not be created for writing. */ static void writeStringToFile(final String string, final File file) throws IOException { createFile(file); PrintWriter printWriter = new PrintWriter(file); printWriter.print(string); printWriter.close(); } /** * Creates a file including parent directories if it does not yet exist. * @param file The file to create * @throws IOException Failed to create the file */ private static void createFile(File file) throws IOException { if (!file.exists()) { createDirectory(file.getParent()); if (!file.createNewFile()) { throw new IOException("Failed to create " + file.getPath()); } } } /** * Returns the classpath for the class loader and its parent. @@ -171,6 +203,47 @@ debugClassPathElements(classLoader.getParent(), log); } /** FreeMarker template configuration. */ static Configuration configuration; /** * Returns a FreeMarker configuration for applying templates. * @return A FreeMarker configuration for applying templates. */ static Configuration getConfiguration() { if (configuration == null) { configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); configuration.setClassForTemplateLoading(Utils.class, "/templates"); configuration.setDefaultEncoding("UTF-8"); configuration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER); } return configuration; } /** * Returns the String result from applying a FreeMarker template. * @param template The name of a template file found in {@code resources/templates/}. * @param map The map holding the data to use in the template. * @return The String result from applying a FreeMarker template. */ static String applyTemplate(final String template, final Map<String, Object> map) { // FreeMarker requires a configuration to find the template. configuration = getConfiguration(); // FreeMarker takes the data and a Writer to process the template. ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Writer writer = new OutputStreamWriter(outputStream); try { Template configurationTemplate = configuration.getTemplate(template); configurationTemplate.process(map, writer); return outputStream.toString(); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { org.forgerock.util.Utils.closeSilently(writer, outputStream); } } private Utils() { // Not used. } opendj-doc-maven-plugin/src/main/resources/templates/appendix-ldap-result-codes.ftl
New file @@ -0,0 +1,82 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- ! CCPL HEADER START ! ! This work is licensed under the Creative Commons ! Attribution-NonCommercial-NoDerivs 3.0 Unported License. ! To view a copy of this license, visit ! http://creativecommons.org/licenses/by-nc-nd/3.0/ ! or send a letter to Creative Commons, 444 Castro Street, ! Suite 900, Mountain View, California, 94041, USA. ! ! You can also obtain a copy of the license at legal-notices/CC-BY-NC-ND.txt. ! See the License for the specific language governing permissions ! and limitations under the License. ! ! If applicable, add the following below this CCPL HEADER, with the fields ! enclosed by brackets "[]" replaced with your own identifying information: ! Portions Copyright [yyyy] [name of copyright owner] ! ! CCPL HEADER END ! ! Copyright ${year} ForgeRock AS. ! --> <#-- Comment text comes from the Javadoc, so the language is English. --> <appendix xml:id="appendix-ldap-result-codes" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd" xmlns:xlink="http://www.w3.org/1999/xlink"> <title>LDAP Result Codes</title> <para> ${classComment} </para> <indexterm> <primary>LDAP</primary> <secondary>Result codes</secondary> </indexterm> <table pgwide="1"> <title>OpenDJ LDAP Result Codes</title> <tgroup cols="3"> <colspec colnum="1" colwidth="1*" /> <colspec colnum="2" colwidth="2*" /> <colspec colnum="3" colwidth="3*" /> <thead> <row> <entry>Result Code</entry> <entry>Name</entry> <entry>Description</entry> </row> </thead> <tbody> <#list resultCodes as resultCode> <row valign="top"> <entry> <para> ${resultCode.intValue} </para> </entry> <entry> <para> ${resultCode.name} </para> </entry> <entry> <para> ${resultCode.comment} </para> </entry> </row> </#list> </tbody> </tgroup> </table> </appendix> opendj-server-legacy/pom.xml
@@ -1950,6 +1950,18 @@ </messageFileNames> </configuration> </execution> <execution> <id>generate-result-code-doc</id> <phase>pre-site</phase> <goals> <goal>generate-result-code-doc</goal> </goals> <configuration> <resultCodeSource>../opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultCode.java</resultCodeSource> <xmlFile>${project.build.directory}/docbkx-sources/reference/appendix-ldap-result-codes.xml</xmlFile> </configuration> </execution> </executions> </plugin> opendj-server-legacy/src/main/docbkx/reference/index.xml
@@ -194,6 +194,14 @@ <xinclude:include href="../shared/glossary.xml" /> <xinclude:include href="appendix-rest2ldap.xml" /> <xinclude:include href="appendix-ldap-result-codes.xml"> <xinclude:fallback> <appendix> <title>LDAP Result Codes Missing</title> <para>The generated LDAP result codes appendix is missing.</para> </appendix> </xinclude:fallback> </xinclude:include> <xinclude:include href='appendix-file-layout.xml' /> <xinclude:include href='appendix-ports-used.xml' /> <xinclude:include href='appendix-standards.xml' /> pom.xml
@@ -196,6 +196,26 @@ </plugin> <plugin> <groupId>org.forgerock.opendj</groupId> <artifactId>opendj-doc-maven-plugin</artifactId> <version>${project.version}</version> <inherited>false</inherited> <executions> <execution> <id>generate-result-code-doc</id> <phase>pre-site</phase> <goals> <goal>generate-result-code-doc</goal> </goals> <configuration> <resultCodeSource>opendj-core/src/main/java/org/forgerock/opendj/ldap/ResultCode.java</resultCodeSource> <xmlFile>${project.build.directory}/docbkx-sources/dev-guide/appendix-ldap-result-codes.xml</xmlFile> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.forgerock.commons</groupId> <artifactId>forgerock-doc-maven-plugin</artifactId> <inherited>false</inherited> src/main/docbkx/dev-guide/index.xml
@@ -131,6 +131,14 @@ </xinclude:include> </reference> <xinclude:include href="appendix-ldap-result-codes.xml"> <xinclude:fallback> <appendix> <title>LDAP Result Codes Missing</title> <para>The generated LDAP result codes appendix is missing.</para> </appendix> </xinclude:fallback> </xinclude:include> <xinclude:include href="../shared/glossary.xml" /> <index />