Github user bbende commented on a diff in the pull request: https://github.com/apache/nifi/pull/3192#discussion_r237917798 --- Diff: nifi-api/src/main/java/org/apache/nifi/documentation/AbstractDocumentationWriter.java --- @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.nifi.documentation; + +import org.apache.nifi.annotation.behavior.DynamicProperties; +import org.apache.nifi.annotation.behavior.DynamicProperty; +import org.apache.nifi.annotation.behavior.DynamicRelationship; +import org.apache.nifi.annotation.behavior.InputRequirement; +import org.apache.nifi.annotation.behavior.ReadsAttribute; +import org.apache.nifi.annotation.behavior.ReadsAttributes; +import org.apache.nifi.annotation.behavior.Restricted; +import org.apache.nifi.annotation.behavior.Stateful; +import org.apache.nifi.annotation.behavior.SystemResourceConsideration; +import org.apache.nifi.annotation.behavior.WritesAttribute; +import org.apache.nifi.annotation.behavior.WritesAttributes; +import org.apache.nifi.annotation.documentation.CapabilityDescription; +import org.apache.nifi.annotation.documentation.DeprecationNotice; +import org.apache.nifi.annotation.documentation.SeeAlso; +import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.components.ConfigurableComponent; +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.controller.ControllerService; +import org.apache.nifi.documentation.init.DocumentationControllerServiceInitializationContext; +import org.apache.nifi.documentation.init.DocumentationProcessorInitializationContext; +import org.apache.nifi.documentation.init.DocumentationReportingInitializationContext; +import org.apache.nifi.processor.Processor; +import org.apache.nifi.processor.Relationship; +import org.apache.nifi.reporting.InitializationException; +import org.apache.nifi.reporting.ReportingTask; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Base class for DocumentationWriter that simplifies iterating over all information for a component, creating a separate method + * for each, to ensure that implementations properly override all methods and therefore properly account for all information about + * a component. + * + * Please note that while this class lives within the nifi-api, it is provided primarily as a means for documentation components within + * the NiFi NAR Maven Plugin. Its home is the nifi-api, however, because the API is needed in order to extract the relevant information and + * the NAR Maven Plugin cannot have a direct dependency on nifi-api (doing so would cause a circular dependency). By having this homed within + * the nifi-api, the Maven plugin is able to discover the class dynamically and invoke the one or two methods necessary to create the documentation. + * + * This is a new capability in 1.9.0 in preparation for the Extension Registry and therefore, you should + * <b>NOTE WELL:</b> At this time, while this class is part of nifi-api, it is still evolving and may change in a non-backward-compatible manner or even be + * removed from one incremental release to the next. Use at your own risk! + */ +public abstract class AbstractDocumentationWriter implements DocumentationWriter { + + @Override + public final void write(final ConfigurableComponent component) throws IOException { + write(component, null); + } + + @Override + public final void write(final ConfigurableComponent component, final Collection<ProvidedServiceAPI> providedServices) throws IOException { + initialize(component); + + writeHeader(component); + writeBody(component); + + if (providedServices != null && component instanceof ControllerService) { + writeProvidedServices(providedServices); + } + + writeFooter(component); + } + + private void initialize(final ConfigurableComponent component) { + try { + if (component instanceof Processor) { + initialize((Processor) component); + } else if (component instanceof ControllerService) { + initialize((ControllerService) component); + } else if (component instanceof ReportingTask) { + initialize((ReportingTask) component); + } + } catch (final InitializationException ie) { + throw new RuntimeException("Failed to initialize " + component, ie); + } + } + + protected void initialize(final Processor processor) { + processor.initialize(new DocumentationProcessorInitializationContext()); + } + + protected void initialize(final ControllerService service) throws InitializationException { + service.initialize(new DocumentationControllerServiceInitializationContext()); + } + + protected void initialize(final ReportingTask reportingTask) throws InitializationException { + reportingTask.initialize(new DocumentationReportingInitializationContext()); + } + + protected void writeBody(final ConfigurableComponent component) throws IOException { + final String additionalDetails = getAdditionalDetailsHtml(component); + + writeExtensionName(component.getClass().getName()); + writeDeprecationNotice(component.getClass().getAnnotation(DeprecationNotice.class)); + writeDescription(getDescription(component)); + writeTags(getTags(component)); + writeProperties(component.getPropertyDescriptors()); + writeDynamicProperties(getDynamicProperties(component)); + + if (component instanceof Processor) { + final Processor processor = (Processor) component; + + writeRelationships(processor.getRelationships()); + writeDynamicRelationship(getDynamicRelationship(processor)); + writeReadsAttributes(getReadsAttributes(processor)); + writeWritesAttributes(getWritesAttributes(processor)); + } + + writeStatefulInfo(component.getClass().getAnnotation(Stateful.class)); + writeRestrictedInfo(component.getClass().getAnnotation(Restricted.class)); + writeInputRequirementInfo(getInputRequirement(component)); + writeSystemResourceConsiderationInfo(getSystemResourceConsiderations(component)); + writeSeeAlso(component.getClass().getAnnotation(SeeAlso.class)); + writeAdditionalDetails(additionalDetails); + } + + protected String getAdditionalDetailsHtml(final ConfigurableComponent component) throws IOException { --- End diff -- Obviously haven't made it very far yet, but does it make sense to take the whole additionalDetails.html as a string since it is a full html document? Maybe this is being parsed later to extract only the body content. Also, in the line below should it use component.getClass().getCanonicalName() ? I checked one of the extension descriptors that should have additional details and the XML had an empty <additionalDetails></additionalDetails>.
---