HC2VPP-180 - Doc coverage generator 40/7640/7
authorJan Srnicek <[email protected]>
Fri, 21 Jul 2017 13:10:46 +0000 (15:10 +0200)
committerMarek Gradzki <[email protected]>
Tue, 25 Jul 2017 10:28:32 +0000 (12:28 +0200)
TODO - links to specific vpp api section(now points
just to section with apis)
TODO - links to specific java binding code(now
points to class thats doing binding)
TODO - operational coverage(ASM does not support lambda processing)
TODO - generate coverage adoc links

Change-Id: I44c85012da3bd2e7cdd41930753e5aae6955cd7b
Signed-off-by: Jan Srnicek <[email protected]>
Signed-off-by: Marek Gradzki <[email protected]>
45 files changed:
acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/AclModule.java
ioam/impl/src/main/java/io/fd/hc2vpp/vppioam/impl/VppIoamModule.java
nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/NatModule.java
nsh/impl/src/main/java/io/fd/hc2vpp/vppnsh/impl/VppNshModule.java
release-notes/pom.xml
release-notes/src/main/asciidoc/api_docs/api_docs.adoc [new file with mode: 0644]
release-notes/src/main/asciidoc/api_docs/api_docs_index.adoc [new file with mode: 0644]
release-notes/src/main/asciidoc/release_notes.adoc
vpp-integration/api-docs/api/asciidoc/Readme.adoc [new file with mode: 0644]
vpp-integration/api-docs/api/pom.xml [new file with mode: 0644]
vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/CoverageUnit.java [new file with mode: 0644]
vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/JavaApiMessage.java [new file with mode: 0644]
vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/Operation.java [new file with mode: 0644]
vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/PluginCoverage.java [new file with mode: 0644]
vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/VppApiMessage.java [new file with mode: 0644]
vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/YangType.java [new file with mode: 0644]
vpp-integration/api-docs/asciidoc/Readme.adoc [new file with mode: 0644]
vpp-integration/api-docs/core/asciidoc/Readme.adoc [new file with mode: 0644]
vpp-integration/api-docs/core/pom.xml [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ClassPathTypeIndex.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CollectingWriterBuilder.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageGenerator.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageScanner.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/LinkGenerator.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodDelegatingClassVisitor.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodPluginCoverageVisitor.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelLinkIndex.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelTypeIndex.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/PluginMethodReference.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/VppApiUtils.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangModelKey.java [new file with mode: 0644]
vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangTypeLinkIndex.java [new file with mode: 0644]
vpp-integration/api-docs/docs/pom.xml [new file with mode: 0644]
vpp-integration/api-docs/pom.xml [new file with mode: 0644]
vpp-integration/api-docs/scripts/pom.xml [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/groovy/io/fd/hc2vpp/docs/scripts/ApiDocsIndexGenerator.groovy [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockAclModule.java [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockBindingModule.java [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockIoamModule.java [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNatModule.java [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNshModule.java [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/resources/routing.json [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/resources/vpp-management.json [new file with mode: 0644]
vpp-integration/api-docs/scripts/src/main/resources/yang_to_jvpp_template [new file with mode: 0644]
vpp-integration/pom.xml

index a97f57e..afb7da6 100644 (file)
@@ -52,7 +52,7 @@ public class AclModule extends AbstractModule {
     }
 
     @VisibleForTesting
-    AclModule(@Nonnull final Class<? extends Provider<FutureJVppAclFacade>> jvppAclProviderClass) {
+    protected AclModule(@Nonnull final Class<? extends Provider<FutureJVppAclFacade>> jvppAclProviderClass) {
         this.jvppAclProviderClass = jvppAclProviderClass;
     }
 
index 4c14313..1feaf9a 100755 (executable)
@@ -40,7 +40,7 @@ import org.slf4j.LoggerFactory;
 /*
  * Glue code necessary for Honeycomb distribution to pick up the plugin classes
  */
-public final class VppIoamModule extends AbstractModule {
+public class VppIoamModule extends AbstractModule {
 
     private static final Logger LOG = LoggerFactory.getLogger(VppIoamModule.class);
     private final Class<? extends Provider<FutureJVppIoamtraceFacade>> jvppIoamTraceProviderClass;
@@ -52,7 +52,7 @@ public final class VppIoamModule extends AbstractModule {
     }
 
     @VisibleForTesting
-    VppIoamModule(Class<? extends Provider<FutureJVppIoamtraceFacade>> jvppIoamTraceProvider,
+    protected VppIoamModule(Class<? extends Provider<FutureJVppIoamtraceFacade>> jvppIoamTraceProvider,
                   Class<? extends Provider<FutureJVppIoampotFacade>> jvppIoamPotProviderClass,
                   Class<? extends Provider<FutureJVppIoamexportFacade>> jvppIoamExportProviderClass) {
         this.jvppIoamTraceProviderClass = jvppIoamTraceProvider;
index cf6c3bc..fece7b0 100644 (file)
@@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
 /**
  * Module class instantiating nat plugin components.
  */
-public final class NatModule extends AbstractModule {
+public class NatModule extends AbstractModule {
 
     private static final Logger LOG = LoggerFactory.getLogger(NatModule.class);
     private final Class<? extends Provider<FutureJVppSnatFacade>> jvppSnatProviderClass;
@@ -48,7 +48,7 @@ public final class NatModule extends AbstractModule {
     }
 
     @VisibleForTesting
-    NatModule(Class<? extends Provider<FutureJVppSnatFacade>> jvppSnatProvider) {
+    protected NatModule(Class<? extends Provider<FutureJVppSnatFacade>> jvppSnatProvider) {
         this.jvppSnatProviderClass = jvppSnatProvider;
     }
 
index 1964faf..aaa1d6c 100755 (executable)
@@ -36,7 +36,7 @@ import org.slf4j.LoggerFactory;
 /**
  * This is some glue code necessary for Honeycomb distribution to pick up the plugin classes
  */
-public final class VppNshModule extends AbstractModule {
+public class VppNshModule extends AbstractModule {
 
     private static final Logger LOG = LoggerFactory.getLogger(VppNshModule.class);
     private final Class<? extends Provider<FutureJVppNshFacade>> jvppNshProviderClass;
@@ -45,7 +45,7 @@ public final class VppNshModule extends AbstractModule {
         this(JVppNshProvider.class);
     }
     @VisibleForTesting
-    VppNshModule(Class<? extends Provider<FutureJVppNshFacade>> jvppNshProvider) {
+    protected VppNshModule(Class<? extends Provider<FutureJVppNshFacade>> jvppNshProvider) {
         this.jvppNshProviderClass = jvppNshProvider;
     }
 
index 1fc670f..c34c7b4 100644 (file)
   <modelVersion>4.0.0</modelVersion>
   <description>Hc2vpp release notes</description>
 
+  <dependencies>
+    <dependency>
+        <groupId>io.fd.hc2vpp.docs</groupId>
+        <artifactId>docs</artifactId>
+        <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
   <!-- Do not push, this is just a documentation producer -->
   <build>
     <plugins>
       </plugin>
 
       <plugin>
+         <groupId>org.apache.maven.plugins</groupId>
+         <artifactId>maven-dependency-plugin</artifactId>
+         <version>2.10</version>
+         <executions>
+            <execution>
+                <id>unpack-configuration</id>
+                <phase>generate-sources</phase>
+                <goals>
+                    <goal>unpack-dependencies</goal>
+                </goals>
+                <configuration>
+                    <includes>*.adoc</includes>
+                    <outputDirectory>${project.build.directory}/raw-adoc/api_docs</outputDirectory>
+                </configuration>
+            </execution>
+         </executions>
+      </plugin>
+
+     <plugin>
         <groupId>org.asciidoctor</groupId>
         <artifactId>asciidoctor-maven-plugin</artifactId>
         <version>${asciidoctor.maven.plugin.version}</version>
@@ -63,6 +90,9 @@
             <configuration>
               <!-- https://github.com/asciidoctor/asciidoctor-maven-plugin -->
               <!-- http://asciidoctor.org/docs/asciidoc-syntax-quick-reference -->
+              <!-- raw-adoc folder combines docs from src folder + generated api docs files,
+              in order to have them all available while generating site -->
+              <baseDir>${project.build.directory}/raw-adoc</baseDir>
               <backend>html5</backend>
               <doctype>docbook</doctype>
               <sourceHighlighter>coderay</sourceHighlighter>
       <plugin>
         <artifactId>maven-resources-plugin</artifactId>
         <executions>
+          <execution>
+            <id>copy-raw-adoc</id>
+              <phase>generate-sources</phase>
+              <goals>
+                <goal>copy-resources</goal>
+              </goals>
+              <configuration>
+                <outputDirectory>${project.build.directory}/raw-adoc</outputDirectory>
+                <resources>
+                  <resource>
+                    <directory>${project.basedir}/src/main/asciidoc/</directory>
+                  </resource>
+                </resources>
+             </configuration>
+          </execution>
           <execution>
             <id>copy-release-notes-to-site</id>
             <phase>site</phase>
diff --git a/release-notes/src/main/asciidoc/api_docs/api_docs.adoc b/release-notes/src/main/asciidoc/api_docs/api_docs.adoc
new file mode 100644 (file)
index 0000000..5d519c6
--- /dev/null
@@ -0,0 +1,3 @@
+== API documentation
+
+link:api_docs_index.html[VPP API to Yang index]
diff --git a/release-notes/src/main/asciidoc/api_docs/api_docs_index.adoc b/release-notes/src/main/asciidoc/api_docs/api_docs_index.adoc
new file mode 100644 (file)
index 0000000..08dfeb2
--- /dev/null
@@ -0,0 +1,19 @@
+== VPP api to Yang index
+
+// TODO - generate this file based on list of JVPP plugins used
+
+Mapping between VPP binary apis and JVpp binding in hc2vpp
+
+include::api_docs/futurejvppcore-yang-config-index.adoc[JVpp Core plugin docs]
+
+include::api_docs/futurejvppacl-yang-config-index.adoc[JVpp Acl plugin docs]
+
+include::api_docs/futurejvppsnat-yang-config-index.adoc[JVpp Snat plugin docs]
+
+include::api_docs/futurejvppnsh-yang-config-index.adoc[JVpp Nsh plugin docs]
+
+include::api_docs/futurejvppioamtrace-yang-config-index.adoc[JVpp Ioam Trace plugin docs]
+
+include::api_docs/futurejvppioampot-yang-config-index.adoc[JVpp Ioam Pot plugin docs]
+
+include::api_docs/futurejvppioamexport-yang-config-index.adoc[JVpp Ioam Export plugin docs]
index 7471cda..7f9c84a 100644 (file)
@@ -26,4 +26,6 @@ include::install_guide/install_guide.adoc[]
 
 include::user_guide/user_guide.adoc[]
 
-include::devel_guide/devel_guide.adoc[]
\ No newline at end of file
+include::devel_guide/devel_guide.adoc[]
+
+include::api_docs/api_docs.adoc[]
\ No newline at end of file
diff --git a/vpp-integration/api-docs/api/asciidoc/Readme.adoc b/vpp-integration/api-docs/api/asciidoc/Readme.adoc
new file mode 100644 (file)
index 0000000..1454049
--- /dev/null
@@ -0,0 +1,13 @@
+= docs-api
+
+Defines general api for VPP binary api coverage
+
+== Elements
+
+* PluginCoverage - Contains coverage data for single JVPP plugin, including supported
+ VPP binary equivalents in JVpp, what Yang nodes are bind to such api and what operations are
+ supported
+* VppApiMessage - Reference to single VPP binary api with link to its documentation
+* JavaApiMessage - Reference to JVpp equivalent of VPP binary api
+* YangType - Reference to single Yang type with link to its model
+* Operation - reference to single CRUD operation with link to binding class
\ No newline at end of file
diff --git a/vpp-integration/api-docs/api/pom.xml b/vpp-integration/api-docs/api/pom.xml
new file mode 100644 (file)
index 0000000..9c40d1f
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2017 Cisco and/or its affiliates.
+  ~
+  ~ 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.
+  -->
+
+<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">
+    <parent>
+        <artifactId>hc2vpp-parent</artifactId>
+        <groupId>io.fd.hc2vpp.common</groupId>
+        <version>1.17.10-SNAPSHOT</version>
+        <relativePath>../../../common/hc2vpp-parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>docs-api</artifactId>
+    <groupId>io.fd.hc2vpp.docs</groupId>
+    <version>1.17.10-SNAPSHOT</version>
+
+</project>
\ No newline at end of file
diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/CoverageUnit.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/CoverageUnit.java
new file mode 100644 (file)
index 0000000..06cb59f
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.api;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents mapping between single supported VPP binary api and its binding
+ */
+public class CoverageUnit {
+
+    /**
+     * VPP binary api reference
+     */
+    private final VppApiMessage vppApi;
+
+    /**
+     * Java equivalent of VPP binary api
+     */
+    private final JavaApiMessage javaApi;
+
+    /**
+     * Yang types used to bind this request
+     */
+    private final List<YangType> yangTypes;
+
+    /**
+     * Operations supported for this api
+     */
+    private final List<Operation> supportedOperations;
+
+    private CoverageUnit(final VppApiMessage vppApi, final JavaApiMessage javaApi,
+                         final List<YangType> yangTypes,
+                         final List<Operation> supportedOperations) {
+        this.vppApi = vppApi;
+        this.javaApi = javaApi;
+        this.yangTypes = yangTypes;
+        this.supportedOperations = supportedOperations;
+    }
+
+    public VppApiMessage getVppApi() {
+        return vppApi;
+    }
+
+    public JavaApiMessage getJavaApi() {
+        return javaApi;
+    }
+
+    public List<YangType> getYangTypes() {
+        return yangTypes;
+    }
+
+    public List<Operation> getSupportedOperations() {
+        return supportedOperations;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final CoverageUnit that = (CoverageUnit) o;
+
+        return Objects.equals(this.vppApi, that.vppApi) &&
+                Objects.equals(this.javaApi, that.javaApi) &&
+                Objects.equals(this.yangTypes, that.yangTypes) &&
+                Objects.equals(this.supportedOperations, that.supportedOperations);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(vppApi, javaApi, yangTypes, supportedOperations);
+    }
+
+    public static class CoverageUnitBuilder {
+        private VppApiMessage vppApi;
+        private JavaApiMessage javaApi;
+        private List<YangType> yangTypes;
+        private List<Operation> supportedOperations;
+
+        public CoverageUnitBuilder setVppApi(final VppApiMessage vppApi) {
+            this.vppApi = vppApi;
+            return this;
+        }
+
+        public CoverageUnitBuilder setJavaApi(final JavaApiMessage javaApi) {
+            this.javaApi = javaApi;
+            return this;
+        }
+
+        public CoverageUnitBuilder setYangTypes(final List<YangType> yangTypes) {
+            this.yangTypes = yangTypes;
+            return this;
+        }
+
+        public CoverageUnitBuilder setSupportedOperations(final List<Operation> supportedOperations) {
+            this.supportedOperations = supportedOperations;
+            return this;
+        }
+
+        public CoverageUnit build() {
+            return new CoverageUnit(vppApi, javaApi, yangTypes, supportedOperations);
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/JavaApiMessage.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/JavaApiMessage.java
new file mode 100644 (file)
index 0000000..db4575c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.api;
+
+import java.util.Objects;
+
+/**
+ * Reference of Java equivalent of VPP binary api
+ */
+public class JavaApiMessage {
+
+    /**
+     * Name of the call
+     */
+    private final String api;
+
+    public JavaApiMessage(final String api) {
+        this.api = api;
+    }
+
+    public String getApi() {
+        return api;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final JavaApiMessage that = (JavaApiMessage) o;
+        return Objects.equals(this.api, that.api);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(api);
+    }
+}
diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/Operation.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/Operation.java
new file mode 100644 (file)
index 0000000..e471fb6
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.api;
+
+import java.util.Objects;
+
+/**
+ * Reference to single crud operation
+ */
+public class Operation {
+
+    /**
+     * Git link to class that performs referenced operation
+     */
+    private final String link;
+    //TODO - investigate option to link directly to line number
+
+    /**
+     * Type of crud operation
+     */
+    private final CrudOperation operation;
+
+    public Operation(final String link, final CrudOperation operation) {
+        this.link = link;
+        this.operation = operation;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+    public CrudOperation getOperation() {
+        return operation;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final Operation that = (Operation) o;
+
+        return Objects.equals(this.link, that.link) &&
+                Objects.equals(this.operation, that.operation);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(link, operation);
+    }
+
+    public enum CrudOperation {
+        WRITE("Write", "writeCurrentAttributes"),
+        UPDATE("Update", "updateCurrentAttributes"),
+        DELETE("Delete", "deleteCurrentAttributes"),
+        READ_ALL("Read all", "getAllIds"),
+        READ_ONE("Read details", "readCurrentAttributes");
+
+        private final String displayName;
+        private final String methodReference;
+
+        CrudOperation(final String displayName, final String methodReference) {
+            this.displayName = displayName;
+            this.methodReference = methodReference;
+        }
+
+        public String getMethodReference() {
+            return methodReference;
+        }
+
+        public String getDisplayName() {
+            return displayName;
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/PluginCoverage.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/PluginCoverage.java
new file mode 100644 (file)
index 0000000..8e62e9c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.api;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents coverage data for single VPP plugin
+ */
+public class PluginCoverage {
+
+    /**
+     * Name of the covered plugin
+     */
+    private final String pluginName;
+
+    /**
+     * Coverage data
+     */
+    private final Set<CoverageUnit> coverage;
+
+    /**
+     * Whether this is config or operational coverage
+     */
+    private final boolean isConfig;
+
+    public PluginCoverage(final String pluginName, final Set<CoverageUnit> coverage, final boolean isConfig) {
+        this.pluginName = pluginName;
+        this.coverage = coverage;
+        this.isConfig = isConfig;
+    }
+
+    public String getPluginName() {
+        return pluginName;
+    }
+
+    public Set<CoverageUnit> getCoverage() {
+        return coverage;
+    }
+
+    public boolean isConfig() {
+        return isConfig;
+    }
+
+    public boolean hasCoverage() {
+        return !coverage.isEmpty();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final PluginCoverage that = (PluginCoverage) o;
+
+        return Objects.equals(this.isConfig, that.isConfig) &&
+                Objects.equals(this.pluginName, that.pluginName) &&
+                Objects.equals(this.coverage, that.coverage);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.pluginName, this.coverage, this.isConfig);
+    }
+}
+
diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/VppApiMessage.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/VppApiMessage.java
new file mode 100644 (file)
index 0000000..7801047
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.api;
+
+import java.util.Objects;
+
+/**
+ * Represents reference to VPP binary api
+ */
+public class VppApiMessage {
+
+    /**
+     * Name of the api
+     */
+    private final String name;
+
+    /**
+     * fd.io doc link
+     */
+    // TODO - check if possible to add direct link for specific api
+    private final String link;
+
+    public VppApiMessage(final String name, final String link) {
+        this.name = name;
+        this.link = link;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final VppApiMessage that = (VppApiMessage) o;
+
+        return Objects.equals(this.name, that.name) && Objects.equals(this.link, that.link);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, link);
+    }
+}
diff --git a/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/YangType.java b/vpp-integration/api-docs/api/src/main/java/io/fd/hc2vpp/docs/api/YangType.java
new file mode 100644 (file)
index 0000000..a2585ac
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.api;
+
+import java.util.Objects;
+
+/**
+ * Represents Yang type used to bind VPP api's
+ */
+public class YangType {
+
+    /**
+     * Fully qualified name of Yang type
+     */
+    private final String type;
+
+    /**
+     * Git link to its model origin
+     */
+    private final String link;
+
+    public YangType(final String type, final String link) {
+        this.type = type;
+        this.link = link;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getLink() {
+        return link;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final YangType that = (YangType) o;
+        return Objects.equals(this.type, that.type) && !Objects.equals(this.link, that.link);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, link);
+    }
+}
diff --git a/vpp-integration/api-docs/asciidoc/Readme.adoc b/vpp-integration/api-docs/asciidoc/Readme.adoc
new file mode 100644 (file)
index 0000000..b0964c2
--- /dev/null
@@ -0,0 +1,3 @@
+= api-docs
+
+Overview of api-docs
\ No newline at end of file
diff --git a/vpp-integration/api-docs/core/asciidoc/Readme.adoc b/vpp-integration/api-docs/core/asciidoc/Readme.adoc
new file mode 100644 (file)
index 0000000..919d9c4
--- /dev/null
@@ -0,0 +1,3 @@
+= docs-core
+
+Overview of docs-core
\ No newline at end of file
diff --git a/vpp-integration/api-docs/core/pom.xml b/vpp-integration/api-docs/core/pom.xml
new file mode 100644 (file)
index 0000000..0d691b4
--- /dev/null
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2017 Cisco and/or its affiliates.
+  ~
+  ~ 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.
+  -->
+
+<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">
+    <parent>
+        <artifactId>hc2vpp-parent</artifactId>
+        <groupId>io.fd.hc2vpp.common</groupId>
+        <version>1.17.10-SNAPSHOT</version>
+        <relativePath>../../../common/hc2vpp-parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>io.fd.hc2vpp.docs</groupId>
+    <artifactId>docs-core</artifactId>
+    <version>1.17.10-SNAPSHOT</version>
+
+    <properties>
+        <mockito-core.version>1.10.19</mockito-core.version>
+        <junit.version>4.12</junit.version>
+        <guice.version>4.1.0</guice.version>
+        <asm.version>5.2</asm.version>
+        <asciidoctorj.version>1.5.4</asciidoctorj.version>
+        <slf4j-simple.version>1.7.25</slf4j-simple.version>
+        <jvpp.version>17.10-SNAPSHOT</jvpp.version>
+        <guava.version>19.0</guava.version>
+        <reflections.version>0.9.11</reflections.version>
+
+        <coverage.modules/>
+        <!-- If covered project is lower that 2 levels under root, must be overridden -->
+        <project.root.folder>${project.basedir}/../..</project.root.folder>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.docs</groupId>
+            <artifactId>docs-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.fd.vpp</groupId>
+            <artifactId>jvpp-core</artifactId>
+            <version>${jvpp.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.vpp</groupId>
+            <artifactId>jvpp-registry</artifactId>
+            <version>${jvpp.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ow2.asm</groupId>
+            <artifactId>asm</artifactId>
+            <version>${asm.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+            <version>${guice.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>translate-utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.honeycomb</groupId>
+            <artifactId>translate-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito-core.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.reflections</groupId>
+            <artifactId>reflections</artifactId>
+            <version>${reflections.version}</version>
+        </dependency>
+
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ClassPathTypeIndex.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ClassPathTypeIndex.java
new file mode 100644 (file)
index 0000000..e204d63
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+/**
+ * Index of java classes to relative absolute paths within repository. Used to generate Git links for binding classes of
+ * VPP apis
+ */
+public class ClassPathTypeIndex implements LinkGenerator {
+
+    private static final String JAVA_SOURCE_FOLDER = "src/main/java";
+    private static final int JAVA_SOURCE_FOLDER_NAME_LENGTH = JAVA_SOURCE_FOLDER.length() + 1;
+
+    /**
+     * <li>key - fully qualified class name value</li><li>value - path within codebase/repository</li>
+     */
+    private final Map<String, String> index;
+
+    public ClassPathTypeIndex(final String projectRoot, final String version) {
+        index = buildIndex(projectRoot, version);
+    }
+
+    /**
+     * <li>input format - LOCAL_ROOT/hc2vpp/module/src/main/java/fully/qualified/class/name/Class.java</li><li>output
+     * format - fully.qualified.class.name.Class</li>
+     */
+    private static String key(String raw) {
+        return raw.substring(raw.indexOf(JAVA_SOURCE_FOLDER) + JAVA_SOURCE_FOLDER_NAME_LENGTH)
+                .replace("/", ".")
+                .replace(".java", "");
+    }
+
+    public String linkForClass(final String clazz) {
+        return index.get(clazz.replace("/", "."));
+    }
+
+    private Map<String, String> buildIndex(final String projectRoot, final String version) {
+        try {
+            return Files.walk(Paths.get(projectRoot))
+                    .filter(path -> path.toString().contains("src/main/java"))
+                    .filter(path -> path.toString().endsWith(".java"))
+                    .map(Path::toString)
+                    .map(s -> s.replace(projectRoot, ""))
+                    .distinct()
+                    .collect(Collectors.toMap(ClassPathTypeIndex::key, o -> generateLink(o, version)));
+        } catch (IOException e) {
+            throw new IllegalStateException(format("%s not found", projectRoot), e);
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CollectingWriterBuilder.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CollectingWriterBuilder.java
new file mode 100644 (file)
index 0000000..b8fcc8b
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+
+import com.google.common.collect.ImmutableSet;
+import io.fd.honeycomb.translate.ModifiableSubtreeManagerRegistryBuilder;
+import io.fd.honeycomb.translate.write.Writer;
+import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Implementations of builder that collects handlers as they are bind
+ */
+public class CollectingWriterBuilder implements ModifiableWriterRegistryBuilder {
+
+    private final List<Writer<? extends DataObject>> singleNodeHandlers;
+    private final List<MultiNodeWriteHandler> multiNodeWriteHandlers;
+
+    public CollectingWriterBuilder() {
+        singleNodeHandlers = new LinkedList<>();
+        multiNodeWriteHandlers = new LinkedList<>();
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> add(
+            @Nonnull Writer<? extends DataObject> handler) {
+        singleNodeHandlers.add(handler);
+        return this;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> subtreeAdd(
+            @Nonnull Set<InstanceIdentifier<?>> handledChildren, @Nonnull Writer<? extends DataObject> handler) {
+        multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, handledChildren));
+        return this;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> addBefore(
+            @Nonnull Writer<? extends DataObject> handler, @Nonnull InstanceIdentifier<?> relatedType) {
+        multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, Collections.singleton(relatedType)));
+        return this;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> addBefore(
+            @Nonnull Writer<? extends DataObject> handler, @Nonnull Collection<InstanceIdentifier<?>> relatedTypes) {
+        singleNodeHandlers.add(handler);
+        return this;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> subtreeAddBefore(
+            @Nonnull Set<InstanceIdentifier<?>> handledChildren, @Nonnull Writer<? extends DataObject> handler,
+            @Nonnull InstanceIdentifier<?> relatedType) {
+        multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, handledChildren));
+        return null;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> subtreeAddBefore(
+            @Nonnull Set<InstanceIdentifier<?>> handledChildren, @Nonnull Writer<? extends DataObject> handler,
+            @Nonnull Collection<InstanceIdentifier<?>> relatedTypes) {
+        multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, handledChildren));
+        return this;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> addAfter(
+            @Nonnull Writer<? extends DataObject> handler, @Nonnull InstanceIdentifier<?> relatedType) {
+        singleNodeHandlers.add(handler);
+        return this;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> addAfter(
+            @Nonnull Writer<? extends DataObject> handler, @Nonnull Collection<InstanceIdentifier<?>> relatedTypes) {
+        singleNodeHandlers.add(handler);
+        return this;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> subtreeAddAfter(
+            @Nonnull Set<InstanceIdentifier<?>> handledChildren, @Nonnull Writer<? extends DataObject> handler,
+            @Nonnull InstanceIdentifier<?> relatedType) {
+        multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, Collections.singleton(relatedType)));
+        return this;
+    }
+
+    @Override
+    public ModifiableSubtreeManagerRegistryBuilder<Writer<? extends DataObject>> subtreeAddAfter(
+            @Nonnull Set<InstanceIdentifier<?>> handledChildren, @Nonnull Writer<? extends DataObject> handler,
+            @Nonnull Collection<InstanceIdentifier<?>> relatedTypes) {
+        multiNodeWriteHandlers.add(new MultiNodeWriteHandler(handler, handledChildren));
+        return this;
+    }
+
+    public List<Writer<? extends DataObject>> getSingleNodeHandlers() {
+        return singleNodeHandlers;
+    }
+
+    public List<MultiNodeWriteHandler> getMultiNodeWriteHandlers() {
+        return multiNodeWriteHandlers;
+    }
+
+    public static class MultiNodeWriteHandler {
+        private final Writer<? extends DataObject> writer;
+        private final Set<String> handledChildren;
+
+
+        public MultiNodeWriteHandler(Writer<? extends DataObject> writer, Set<InstanceIdentifier<?>> handledChildren) {
+            this.writer = writer;
+            this.handledChildren = ImmutableSet.<String>builder()
+                    .add(writer.getManagedDataObjectType().getTargetType().getName())
+                    .addAll(handledChildren.stream()
+                            .map(InstanceIdentifier::getTargetType)
+                            .map(Class::getName)
+                            .collect(Collectors.toSet()))
+                    .build();
+        }
+
+        public Writer<? extends DataObject> getWriter() {
+            return writer;
+        }
+
+        public Set<String> getHandledChildren() {
+            return handledChildren;
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageGenerator.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageGenerator.java
new file mode 100644 (file)
index 0000000..4b6ab77
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+
+import static io.fd.hc2vpp.docs.api.Operation.CrudOperation.DELETE;
+import static io.fd.hc2vpp.docs.api.Operation.CrudOperation.UPDATE;
+import static io.fd.hc2vpp.docs.api.Operation.CrudOperation.WRITE;
+import static java.lang.String.format;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+import io.fd.hc2vpp.docs.api.CoverageUnit;
+import io.fd.hc2vpp.docs.api.JavaApiMessage;
+import io.fd.hc2vpp.docs.api.Operation;
+import io.fd.hc2vpp.docs.api.PluginCoverage;
+import io.fd.hc2vpp.docs.api.YangType;
+import io.fd.honeycomb.translate.write.WriterFactory;
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.reflections.ReflectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CoverageGenerator implements VppApiUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CoverageGenerator.class);
+
+    private final CollectingWriterBuilder writerBuilder;
+
+    public CoverageGenerator() {
+        writerBuilder = new CollectingWriterBuilder();
+    }
+
+    public PluginCoverage generateConfigCoverage(final Class<?> pluginClass,
+                                                 final String version,
+                                                 final List<Module> scannedModules,
+                                                 final YangTypeLinkIndex yangTypeIndex,
+                                                 final ClassPathTypeIndex classPathIndex) {
+        LOG.info("Generating config coverage for plugin {}", pluginClass);
+        getInjectedWriterFactories(scannedModules).forEach(writerFactory -> writerFactory.init(writerBuilder));
+
+        LOG.info("Processing single node handlers");
+        final Set<CoverageUnit> singleNodeCoverageUnits = writerBuilder.getSingleNodeHandlers().stream()
+                .flatMap(writer -> {
+                    // extracts customizer class from handler
+                    final Class<?> customizerClass = getCustomizerClass(writer);
+
+                    // scans within write method
+                    final Set<PluginMethodReference> writeReferences =
+                            new CoverageScanner(customizerClass, WRITE, pluginClass).scan();
+
+                    // scans within update method
+                    final Set<PluginMethodReference> updateReferences =
+                            new CoverageScanner(customizerClass, UPDATE, pluginClass).scan();
+
+                    // scans within delete method
+                    final Set<PluginMethodReference> deleteReferences =
+                            new CoverageScanner(customizerClass, DELETE, pluginClass).scan();
+
+                    return Stream.of(writeReferences.stream(), updateReferences.stream(), deleteReferences.stream())
+                            .flatMap(pluginMethodReferences -> pluginMethodReferences)
+                            .map(reference -> {
+                                final CoverageUnit.CoverageUnitBuilder builder = new CoverageUnit.CoverageUnitBuilder();
+
+                                // binds vpp api name and generateLink bind with version
+                                builder.setVppApi(fromJvppApi(version, reference));
+
+                                //binds java api reference
+                                builder.setJavaApi(new JavaApiMessage(reference.getName()));
+
+                                //binds Yang types with links from pre-build index
+                                // TODO - use deserialized yii
+                                final String typeName = writer.getManagedDataObjectType().getTargetType().getTypeName();
+                                builder.setYangTypes(Collections.singletonList(new YangType(
+                                        typeName,
+                                        yangTypeIndex.getLinkForType(typeName))));
+
+                                final List<Operation> supportedOperations = new LinkedList<>();
+
+                                final String callerClassLink = classPathIndex.linkForClass(reference.getCaller());
+                                if (writeReferences.contains(reference)) {
+                                    supportedOperations.add(new Operation(callerClassLink, WRITE));
+                                }
+
+                                if (updateReferences.contains(reference)) {
+                                    supportedOperations.add(new Operation(callerClassLink, UPDATE));
+                                }
+
+                                if (deleteReferences.contains(reference)) {
+                                    supportedOperations.add(new Operation(callerClassLink, DELETE));
+                                }
+                                return builder.setSupportedOperations(supportedOperations).build();
+                            });
+                }).collect(Collectors.toSet());
+
+        LOG.info("Processing multi node handlers");
+        final Set<CoverageUnit> multiNodeCoverageUnits = writerBuilder.getMultiNodeWriteHandlers().stream()
+                .flatMap(handler -> {
+                    final Class<?> customizerClass = getCustomizerClass(handler.getWriter());
+                    final Set<PluginMethodReference> writeReferences =
+                            new CoverageScanner(customizerClass, WRITE, pluginClass).scan();
+
+                    final Set<PluginMethodReference> updateReferences =
+                            new CoverageScanner(customizerClass, UPDATE, pluginClass).scan();
+
+                    final Set<PluginMethodReference> deleteReferences =
+                            new CoverageScanner(customizerClass, DELETE, pluginClass).scan();
+
+                    return Stream.of(writeReferences.stream(), updateReferences.stream(), deleteReferences.stream())
+                            .flatMap(pluginMethodReferenceStream -> pluginMethodReferenceStream)
+                            .map(reference -> {
+                                final CoverageUnit.CoverageUnitBuilder builder = new CoverageUnit.CoverageUnitBuilder();
+                                builder.setVppApi(fromJvppApi(version, reference));
+                                builder.setJavaApi(new JavaApiMessage(reference.getName()));
+
+                                builder.setYangTypes(handler.getHandledChildren().stream()
+                                        .map(type -> new YangType(type, yangTypeIndex.getLinkForType(type)))
+                                        .collect(Collectors.toList()));
+
+                                final String callerClassLink = classPathIndex.linkForClass(reference.getCaller());
+                                final List<Operation> supportedOperations = new LinkedList<>();
+                                if (writeReferences.contains(reference)) {
+                                    supportedOperations.add(new Operation(callerClassLink, WRITE));
+                                }
+
+                                if (updateReferences.contains(reference)) {
+                                    supportedOperations.add(new Operation(callerClassLink, UPDATE));
+                                }
+
+                                if (deleteReferences.contains(reference)) {
+                                    supportedOperations.add(new Operation(callerClassLink, DELETE));
+                                }
+                                return builder.setSupportedOperations(supportedOperations).build();
+                            });
+                }).collect(Collectors.toSet());
+
+        return new PluginCoverage(pluginClass.getSimpleName(),
+                Stream.of(singleNodeCoverageUnits.stream(), multiNodeCoverageUnits.stream())
+                        .flatMap(coverageUnitStream -> coverageUnitStream)
+                        .collect(Collectors.toSet()), true);
+    }
+
+    private static Class<?> getCustomizerClass(final Object handler) {
+        try {
+            final Set<Field> customizerFields =
+                    ReflectionUtils.getAllFields(handler.getClass(), field -> "customizer".equals(field.getName()));
+            final Field customizerField = customizerFields.iterator().next();
+            customizerField.setAccessible(true);
+            return customizerField.get(handler).getClass();
+        } catch (IllegalAccessException e) {
+            throw new IllegalStateException(format("Unable to get customizer from %s ", handler), e);
+        }
+    }
+
+    private static Set<WriterFactory> getInjectedWriterFactories(final List<Module> scannedModules) {
+        Injector injector = Guice.createInjector(scannedModules);
+        TypeLiteral<Set<WriterFactory>> writerFactoryType = new TypeLiteral<Set<WriterFactory>>() {
+        };
+        return injector.getInstance(Key.get(writerFactoryType));
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageScanner.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/CoverageScanner.java
new file mode 100644 (file)
index 0000000..c1c67d7
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+
+import static java.lang.String.format;
+
+import io.fd.hc2vpp.docs.api.Operation;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.objectweb.asm.ClassReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Scans provided class for reference under specified crud method
+ */
+public class CoverageScanner {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CoverageScanner.class);
+
+    private final Class<?> classToScan;
+    private final Operation.CrudOperation crudOperation;
+    private final Class<?> referenceClass;
+
+    public CoverageScanner(final Class<?> classToScan,
+                           final Operation.CrudOperation crudOperation,
+                           final Class<?> referenceClass) {
+        this.classToScan = classToScan;
+        this.crudOperation = crudOperation;
+        this.referenceClass = referenceClass;
+    }
+
+    static ClassReader loadClass(String className) {
+        try (InputStream classStream =
+                     CoverageScanner.class.getClassLoader().getResourceAsStream(className + ".class")) {
+            return new ClassReader(classStream);
+        } catch (IOException e) {
+            throw new IllegalStateException(format("Unable to load bytecode for class %s", className), e);
+        }
+    }
+
+    public Set<PluginMethodReference> scan() {
+        LOG.debug("Scanning class {}", classToScan.getName());
+        final ClassReader classReader = loadClass(byteCodeStyleReference(classToScan.getName()));
+        final Set<PluginMethodReference> foundReferences = Collections.synchronizedSet(new HashSet<>());
+        classReader.accept(new MethodDelegatingClassVisitor(byteCodeStyleReference(classToScan.getName()),
+                crudOperation.getMethodReference(), byteCodeStyleReference(referenceClass.getPackage().getName()),
+                foundReferences, null), ClassReader.SKIP_DEBUG);
+        return foundReferences;
+    }
+
+    // converts java style reference to bytecode-style name(with slashes instead of dots)
+    private static String byteCodeStyleReference(final String javaStyleName) {
+        return javaStyleName.replace(".", "/");
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/LinkGenerator.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/LinkGenerator.java
new file mode 100644 (file)
index 0000000..3226639
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+public interface LinkGenerator {
+
+    static String resolveBranch(final String version) {
+        if (version.contains("SNAPSHOT")) {
+            return "master";
+        } else {
+            return "stable%2F" + version.replace(".", "");
+        }
+    }
+
+    default String generateLink(final String raw, final String version) {
+        //https://git.fd.io/hc2vpp/tree/interface-role/api/src/main/yang/[email protected]?h=stable%2F1707
+        return "https://git.fd.io/hc2vpp/tree" + raw + "?h=" + resolveBranch(version);
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodDelegatingClassVisitor.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodDelegatingClassVisitor.java
new file mode 100644 (file)
index 0000000..39a06e6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+
+import java.util.Set;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class MethodDelegatingClassVisitor extends ClassVisitor {
+
+    private final String currentClass;
+    private final String methodName;
+    private final String reference;
+    private final Set<PluginMethodReference> foundReferences;
+    private final Set<String> allreadyProcessedLocalMethods;
+
+    public MethodDelegatingClassVisitor(String currentClass,
+                                        String methodName,
+                                        String reference,
+                                        Set<PluginMethodReference> foundReferences,
+                                        Set<String> allreadyProcessedLocalMethods) {
+        super(Opcodes.ASM5);
+        this.currentClass = currentClass;
+        this.methodName = methodName;
+        this.reference = reference;
+        this.foundReferences = foundReferences;
+        this.allreadyProcessedLocalMethods = allreadyProcessedLocalMethods;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+        if (name.equals(methodName)) {
+            return new MethodPluginCoverageVisitor(currentClass, foundReferences, reference,
+                    allreadyProcessedLocalMethods);
+        }
+        return super.visitMethod(access, name, desc, signature, exceptions);
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodPluginCoverageVisitor.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/MethodPluginCoverageVisitor.java
new file mode 100644 (file)
index 0000000..fe15f5e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+import static java.lang.String.format;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MethodPluginCoverageVisitor extends MethodVisitor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MethodPluginCoverageVisitor.class);
+
+    private final String currentClass;
+    private final Set<PluginMethodReference> foundReferences;
+    private final String reference;
+    private final Set<String> allreadyProcessedLocal;
+
+    public MethodPluginCoverageVisitor(String currentClass, Set<PluginMethodReference> foundReferences,
+                                       String reference,
+                                       Set<String> allreadyProcessedLocal) {
+        super(Opcodes.ASM5);
+        this.currentClass = currentClass;
+        this.foundReferences = foundReferences;
+        this.reference = reference;
+        // if nonnull then reuse
+        this.allreadyProcessedLocal = allreadyProcessedLocal == null
+                ? new HashSet<>()
+                : allreadyProcessedLocal;
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean isInterface) {
+        final String normalizedOwner = owner.replace(";", "").replace("[L", "");
+        if (normalizedOwner.contains(reference)) {
+            // reference was found directly in method code
+            foundReferences.add(new PluginMethodReference(currentClass, owner, name));
+        } else {
+            if (normalizedOwner.contains("io/fd")) {
+                // filter just our method to reduce scope
+                if (!normalizedOwner.equals(currentClass)) {
+                    LOG.debug("Processing non-current {}.{}()", normalizedOwner, name);
+                    // method call is from different class than currently processed one
+                    ClassReader classReader = CoverageScanner.loadClass(normalizedOwner);
+                    classReader.accept(new MethodDelegatingClassVisitor(normalizedOwner, name, reference,
+                            foundReferences,
+                            allreadyProcessedLocal), ClassReader.SKIP_DEBUG);
+                } else {
+                    LOG.debug("Processing current {}.{}()", normalizedOwner, name);
+                    // other methods in same class that are used in visited method
+                    String fullyQualifiedMethodName = fullyQualifiedMethodName(normalizedOwner, name, desc);
+                    if (allreadyProcessedLocal.contains(fullyQualifiedMethodName)) {
+                        //skip already processed local methods to prevent stack overflow
+                        return;
+                    }
+                    allreadyProcessedLocal.add(fullyQualifiedMethodName);
+
+                    ClassReader classReader = CoverageScanner.loadClass(normalizedOwner);
+                    classReader.accept(new MethodDelegatingClassVisitor(normalizedOwner, name, reference,
+                            foundReferences,
+                            allreadyProcessedLocal), ClassReader.SKIP_DEBUG);
+                }
+            }
+        }
+        super.visitMethodInsn(opcode, owner, name, desc, isInterface);
+    }
+
+    private String fullyQualifiedMethodName(String owner, String name, String desc) {
+        return format("%s_%s_%s", owner, name, desc);
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelLinkIndex.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelLinkIndex.java
new file mode 100644 (file)
index 0000000..c1b2998
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Index from module file name to git generateLink
+ */
+class ModelLinkIndex implements LinkGenerator {
+
+    private final Map<String, String> modelLinkIndex;
+
+    /**
+     * @param projectRoot for ex.: /home/jsrnicek/Projects/hc2vpp
+     * @param version     for ex.: 17.07 to get generateLink for correct branch
+     */
+    ModelLinkIndex(final String projectRoot, final String version) {
+        modelLinkIndex = buildIndex(projectRoot, version);
+    }
+
+    private static String key(String raw) {
+        return raw.substring(raw.lastIndexOf("/"))
+                .replace("/", "")
+                .replace(".yang", "");
+    }
+
+    String linkForModel(final String model, final String revision) {
+        // TODO - figure out how to get revision for model in src,to use YangModelKey
+        // if not local model,add generateLink to ietf datatracker
+        return Optional.ofNullable(modelLinkIndex.get(model + "@" + revision))
+                .orElse(Optional.ofNullable(modelLinkIndex.get(model))
+                        .orElse("https://datatracker.ietf.org/"));
+    }
+
+    private Map<String, String> buildIndex(final String projectRoot, final String version) {
+        try {
+            return Files.walk(Paths.get(projectRoot))
+                    .filter(path -> path.toString().contains("src/main/yang"))
+                    .filter(path -> path.toString().endsWith(".yang"))
+                    .map(Path::toString)
+                    .map(s -> s.replace(projectRoot, ""))
+                    .distinct()
+                    .collect(Collectors.toMap(ModelLinkIndex::key, o -> generateLink(o, version)));
+        } catch (IOException e) {
+            throw new IllegalStateException(format("%s not found", projectRoot), e);
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelTypeIndex.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/ModelTypeIndex.java
new file mode 100644 (file)
index 0000000..9fd5976
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+import static java.util.stream.Collectors.toMap;
+
+import com.google.common.io.Resources;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+/**
+ * Maps namespaces to models
+ */
+class ModelTypeIndex {
+
+    private final Map<YangModelKey, String> namespaceToModuleIndex;
+
+    ModelTypeIndex() throws IOException {
+        namespaceToModuleIndex = collectAllModules(this.getClass().getClassLoader())
+                .stream()
+                .collect(toMap(YangModelKey::new, YangModuleInfo::getName));
+    }
+
+    private static YangModelBindingProvider getModuleBindingProviderInstance(final Class<?> aClass) {
+        try {
+            return (YangModelBindingProvider) aClass.newInstance();
+        } catch (InstantiationException | IllegalAccessException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private static Class<?> loadClass(final ClassLoader classLoader, final String name) {
+        try {
+            return classLoader.loadClass(name);
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException(e);
+
+        }
+    }
+
+    private static List<String> loadResource(final URL url) {
+        try {
+            return Resources.readLines(url, StandardCharsets.UTF_8);
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    String namespaceToModule(final String namespace,
+                             final String revision) {
+        return namespaceToModuleIndex.get(new YangModelKey(namespace, revision));
+    }
+
+    private Set<YangModuleInfo> collectAllModules(final ClassLoader classLoader) throws IOException {
+        return Collections.list(classLoader.getResources("META-INF/services/" +
+                YangModelBindingProvider.class.getName()))
+                .stream()
+                .map(ModelTypeIndex::loadResource)
+                .flatMap(Collection::stream)
+                .map(name -> loadClass(classLoader, name))
+                .map(ModelTypeIndex::getModuleBindingProviderInstance)
+                .map(YangModelBindingProvider::getModuleInfo)
+                .collect(Collectors.toSet());
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/PluginMethodReference.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/PluginMethodReference.java
new file mode 100644 (file)
index 0000000..5a98639
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+/**
+ * Represent found reference of plugin method
+ */
+public class PluginMethodReference {
+
+    /**
+     * Name of the class that uses such reference
+     */
+    private final String caller;
+
+    /**
+     * Class of the reference
+     */
+    private final String owner;
+
+    /**
+     * Name of the reference
+     */
+    private final String name;
+
+    public PluginMethodReference(final String caller, final String owner, final String name) {
+        this.caller = caller;
+        this.owner = owner;
+        this.name = name;
+    }
+
+    public String getCaller() {
+        return caller;
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/VppApiUtils.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/VppApiUtils.java
new file mode 100644 (file)
index 0000000..f21cc26
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+import com.google.common.base.CaseFormat;
+import io.fd.hc2vpp.docs.api.VppApiMessage;
+
+
+public interface VppApiUtils {
+
+    static String vppApiFromJavaApi(final String jvppApi) {
+        return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, jvppApi);
+    }
+
+    static String generateVppApiDocLink(final String version, final String vppApi) {
+        //https://docs.fd.io/vpp/17.07/d9/d1d/structvl__api__create__subif__t.html
+        // links are using double underscore
+        //final String doubleUnderscoreApiName = vppApi.replace("_", "__");
+        //return format("https://docs.fd.io/vpp/%s/d9/d1d/structvl__api__%s__t.html", version, doubleUnderscoreApiName);
+
+        // FIXME - generateLink has dynamic part that can be resolved from api name
+        return "https://docs.fd.io/vpp/17.07/annotated.html";
+    }
+
+    default VppApiMessage fromJvppApi(final String version, final PluginMethodReference jvppApi) {
+        final String vppApi = vppApiFromJavaApi(jvppApi.getName());
+        return new VppApiMessage(vppApi, generateVppApiDocLink(version, vppApi));
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangModelKey.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangModelKey.java
new file mode 100644 (file)
index 0000000..2bc5bc5
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+
+final class YangModelKey {
+    private final String namespace;
+    private final String revision;
+
+    YangModelKey(final YangModuleInfo moduleInfo) {
+        this.namespace = moduleInfo.getNamespace();
+        this.revision = moduleInfo.getRevision();
+    }
+
+    YangModelKey(final String namespace, final String revision) {
+        this.namespace = namespace;
+        this.revision = revision;
+    }
+
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public String getRevision() {
+        return revision;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final YangModelKey that = (YangModelKey) o;
+
+        if (namespace != null
+                ? !namespace.equals(that.namespace)
+                : that.namespace != null) {
+            return false;
+        }
+        return revision != null
+                ? revision.equals(that.revision)
+                : that.revision == null;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = namespace != null
+                ? namespace.hashCode()
+                : 0;
+        result = 31 * result + (revision != null
+                ? revision.hashCode()
+                : 0);
+        return result;
+    }
+}
diff --git a/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangTypeLinkIndex.java b/vpp-integration/api-docs/core/src/main/java/io/fd/hc2vpp/docs/core/YangTypeLinkIndex.java
new file mode 100644 (file)
index 0000000..8220d7f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core;
+
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import org.opendaylight.yangtools.yang.common.QName;
+
+public class YangTypeLinkIndex {
+
+    private final ModelLinkIndex modelLinkIndex;
+    private final ModelTypeIndex modelTypeIndex;
+
+    public YangTypeLinkIndex(final String projectRoot, final String version) {
+        modelLinkIndex = new ModelLinkIndex(projectRoot, version);
+        try {
+            modelTypeIndex = new ModelTypeIndex();
+        } catch (IOException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    public String getLinkForType(final String classname) {
+        final Class<?> loadedClass;
+        final QName qname;
+        try {
+            loadedClass = this.getClass().getClassLoader().loadClass(classname);
+            final Field qnameField = loadedClass.getField("QNAME");
+            qname = QName.class.cast(qnameField.get(null));
+        } catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) {
+            throw new IllegalStateException(format("Unable to extract QNAME from %s", classname), e);
+        }
+
+
+        final String namespace = qname.getNamespace().toString();
+        final String formattedRevision = qname.getFormattedRevision();
+        final String model = modelTypeIndex.namespaceToModule(namespace, formattedRevision);
+        return modelLinkIndex.linkForModel(model, formattedRevision);
+    }
+}
diff --git a/vpp-integration/api-docs/docs/pom.xml b/vpp-integration/api-docs/docs/pom.xml
new file mode 100644 (file)
index 0000000..cdd3cf4
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2017 Cisco and/or its affiliates.
+  ~
+  ~ 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.
+  -->
+
+<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>io.fd.hc2vpp.docs</groupId>
+    <artifactId>docs</artifactId>
+    <version>1.17.10-SNAPSHOT</version>
+
+    <properties>
+        <!-- List of modules that are used to extract Writer/Reader factories, and therefore generate api documentation -->
+        <api.docs.modules>
+            io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule,
+            io.fd.hc2vpp.docs.core.mock.binding.MockAclModule,
+            io.fd.hc2vpp.docs.core.mock.binding.MockNshModule,
+            io.fd.hc2vpp.docs.core.mock.binding.MockIoamModule,
+            io.fd.hc2vpp.docs.core.mock.binding.MockNatModule,
+            io.fd.hc2vpp.iface.role.InterfaceRoleModule,
+            io.fd.hc2vpp.l3.InterfaceL3Module,
+            io.fd.hc2vpp.l3.ProxyArpModule,
+            io.fd.hc2vpp.l3.SubInterfaceL3Module,
+            io.fd.hc2vpp.lisp.LispModule,
+            io.fd.hc2vpp.lisp.gpe.GpeModule,
+            io.fd.hc2vpp.management.VppManagementModule,
+            io.fd.hc2vpp.policer.PolicerModule,
+            io.fd.hc2vpp.routing.RoutingModule,
+            io.fd.hc2vpp.v3po.V3poModule,
+            io.fd.hc2vpp.vpp.classifier.InterfaceClassifierAclModule,
+            io.fd.hc2vpp.vpp.classifier.SubInterfaceClassifierAclModule,
+            io.fd.hc2vpp.vpp.classifier.VppClassifierModule,
+            io.fd.hc2vpp.dhcp.DhcpModule,
+            io.fd.hc2vpp.bgp.inet.BgpInetModule
+        </api.docs.modules>
+        <project.root.folder>${project.basedir}/../../..</project.root.folder>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.6.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.gmaven</groupId>
+                <artifactId>groovy-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-coverage-doc</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>execute</goal>
+                        </goals>
+                        <configuration>
+                            <source>
+                                io.fd.hc2vpp.docs.scripts.ApiDocsIndexGenerator.generate(project, log)
+                            </source>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>io.fd.hc2vpp.docs</groupId>
+                        <artifactId>scripts</artifactId>
+                        <version>${project.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/vpp-integration/api-docs/pom.xml b/vpp-integration/api-docs/pom.xml
new file mode 100644 (file)
index 0000000..cb1f1ac
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2017 Cisco and/or its affiliates.
+  ~
+  ~ 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.
+  -->
+
+<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">
+    <parent>
+        <artifactId>hc2vpp-parent</artifactId>
+        <groupId>io.fd.hc2vpp.common</groupId>
+        <version>1.17.10-SNAPSHOT</version>
+        <relativePath>../../common/hc2vpp-parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>io.fd.hc2vpp.docs</groupId>
+    <artifactId>api-docs</artifactId>
+    <packaging>pom</packaging>
+    <version>1.17.10-SNAPSHOT</version>
+
+    <modules>
+        <module>core</module>
+        <module>api</module>
+        <module>docs</module>
+        <module>scripts</module>
+    </modules>
+</project>
\ No newline at end of file
diff --git a/vpp-integration/api-docs/scripts/pom.xml b/vpp-integration/api-docs/scripts/pom.xml
new file mode 100644 (file)
index 0000000..f7b14ff
--- /dev/null
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2017 Cisco and/or its affiliates.
+  ~
+  ~ 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.
+  -->
+
+<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>io.fd.hc2vpp.docs</groupId>
+    <artifactId>scripts</artifactId>
+    <version>1.17.10-SNAPSHOT</version>
+
+    <properties>
+        <groovy.version>2.4.7</groovy.version>
+        <groovy.eclipse.compiler.version>2.9.2-01</groovy.eclipse.compiler.version>
+        <groovy.eclipse.batch.version>2.4.3-01</groovy.eclipse.batch.version>
+
+        <jvpp.version>17.10-SNAPSHOT</jvpp.version>
+        <junit.version>4.12</junit.version>
+        <reflections.version>0.9.11</reflections.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.fd.hc2vpp.docs</groupId>
+            <artifactId>docs-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.groovy</groupId>
+            <artifactId>groovy-all</artifactId>
+            <version>${groovy.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.vpp</groupId>
+            <artifactId>jvpp-registry</artifactId>
+            <version>${jvpp.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.vpp</groupId>
+            <artifactId>jvpp-core</artifactId>
+            <version>${jvpp.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.reflections</groupId>
+            <artifactId>reflections</artifactId>
+            <version>${reflections.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.fd.hc2vpp.acl</groupId>
+            <artifactId>acl-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.nat</groupId>
+            <artifactId>nat2vpp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.ioam</groupId>
+            <artifactId>vppioam-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.nsh</groupId>
+            <artifactId>vppnsh-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.v3po</groupId>
+            <artifactId>v3po2vpp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.lisp</groupId>
+            <artifactId>lisp2vpp</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.management</groupId>
+            <artifactId>vpp-management-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.iface.role</groupId>
+            <artifactId>impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.l3</groupId>
+            <artifactId>l3-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.vpp.classifier</groupId>
+            <artifactId>vpp-classifier-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.routing</groupId>
+            <artifactId>routing-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.dhcp</groupId>
+            <artifactId>dhcp-impl</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.fd.hc2vpp.bgp</groupId>
+            <artifactId>bgp-inet</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Extract plugins and resources from them -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>2.10</version>
+                <executions>
+                    <!-- Dependencies are copied by parent project -->
+                    <execution>
+                        <id>unpack-configuration</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>unpack-dependencies</goal>
+                        </goals>
+                        <configuration>
+                            <includes>**/honeycomb-minimal-resources/</includes>
+                            <outputDirectory>${project.build.outputDirectory}/</outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.groovy</groupId>
+                <artifactId>groovy-eclipse-compiler</artifactId>
+                <version>${groovy.eclipse.compiler.version}</version>
+                <extensions>true</extensions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <!-- 2.8.0-01 and later require maven-compiler-plugin 3.1 or higher -->
+                <configuration>
+                    <compilerId>groovy-eclipse-compiler</compilerId>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.codehaus.groovy</groupId>
+                        <artifactId>groovy-eclipse-compiler</artifactId>
+                        <version>${groovy.eclipse.compiler.version}</version>
+                    </dependency>
+                    <!-- for 2.8.0-01 and later you must have an explicit dependency on groovy-eclipse-batch -->
+                    <dependency>
+                        <groupId>org.codehaus.groovy</groupId>
+                        <artifactId>groovy-eclipse-batch</artifactId>
+                        <version>${groovy.eclipse.batch.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/vpp-integration/api-docs/scripts/src/main/groovy/io/fd/hc2vpp/docs/scripts/ApiDocsIndexGenerator.groovy b/vpp-integration/api-docs/scripts/src/main/groovy/io/fd/hc2vpp/docs/scripts/ApiDocsIndexGenerator.groovy
new file mode 100644 (file)
index 0000000..238f4a2
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.scripts
+
+import groovy.text.SimpleTemplateEngine
+import io.fd.hc2vpp.docs.api.*
+import io.fd.hc2vpp.docs.core.ClassPathTypeIndex
+import io.fd.hc2vpp.docs.core.CoverageGenerator
+import io.fd.hc2vpp.docs.core.YangTypeLinkIndex
+import io.fd.vpp.jvpp.acl.future.FutureJVppAcl
+import io.fd.vpp.jvpp.core.future.FutureJVppCore
+import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexport
+import io.fd.vpp.jvpp.ioampot.future.FutureJVppIoampot
+import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtrace
+import io.fd.vpp.jvpp.nsh.future.FutureJVppNsh
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnat
+
+import java.nio.charset.StandardCharsets
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.nio.file.StandardOpenOption
+import java.util.stream.Collectors
+
+import static java.util.stream.Collectors.toList
+
+/**
+ * Generates VPP api to Yang node index for hc2vpp guice modules listed in api.docs.modules maven property.
+ */
+class ApiDocsIndexGenerator {
+
+    private static def NL = System.lineSeparator()
+    // TODO - check if list of plugin classes can be generated based on list of modules enabled for doc generation
+    private static
+    def PLUGIN_CLASSES = [FutureJVppCore.class, FutureJVppAcl.class, FutureJVppSnat.class, FutureJVppNsh.class,
+                          FutureJVppIoamexport.class, FutureJVppIoampot.class, FutureJVppIoamtrace.class]
+    private static def TABLE_PART_MARK = "|"
+
+    /**
+     * Generate coverage data for all configured coverage.modules and JVpp plugins
+     * */
+    public static void generate(final project, final log) {
+        def loader = this.getClassLoader()
+
+        String moduleNames = project.properties.get("api.docs.modules")
+        String projectRoot = project.properties.get("project.root.folder")
+
+        if (moduleNames.trim().isEmpty()) {
+            log.info "No modules defined for ${project.name}. Skipping api-docs generation."
+            return
+        }
+
+        final List<String> moduleNamesList = moduleNames.split(",")
+
+        log.info "Reading module list for ${project.name}"
+        def modules = moduleNamesList.stream()
+                .map { moduleName -> moduleName.trim() }
+                .map { moduleName ->
+            log.info "Loading class $moduleName"
+            loader.loadClass(moduleName).newInstance()
+        }
+        .collect(toList())
+
+        String outPath = project.build.outputDirectory
+
+        log.info "Generating yang type generateLink index"
+        YangTypeLinkIndex yangTypeIndex = new YangTypeLinkIndex(projectRoot, project.version)
+        log.info "Classpath type generateLink index"
+        ClassPathTypeIndex classPathIndex = new ClassPathTypeIndex(projectRoot, project.version)
+
+        log.info "Generating VPP API to YANG mapping"
+        PLUGIN_CLASSES.stream()
+                .forEach { pluginClass ->
+            log.info "Generating mapping for ${pluginClass}"
+            final PluginCoverage configCoverage = new CoverageGenerator()
+                    .generateConfigCoverage(pluginClass, project.version, modules, yangTypeIndex, classPathIndex)
+            generateJvppCoverageDoc(configCoverage, outPath, log)
+
+            //TODO operational coverage
+        }
+    }
+
+    static void generateJvppCoverageDoc(
+            final PluginCoverage pluginCoverage, final String outPath, final log) {
+        if (!pluginCoverage.hasCoverage()) {
+            log.info "Plugin ${pluginCoverage.getPluginName()} does not have coverage data, skipping config docs generation"
+            return
+        }
+        log.info "Generating config api docs for plugin ${pluginCoverage.getPluginName()}"
+        def template = this.getClassLoader().getResource("yang_to_jvpp_template")
+        def result = new SimpleTemplateEngine()
+                .createTemplate(template)
+                .make(["pluginName": pluginCoverage.getPluginName(), "tableContent": generateConfigTableContent(pluginCoverage.getCoverage())]).toString()
+
+        log.debug "Docs result for ${pluginCoverage.getPluginName()}$NL$result"
+
+        Paths.get(outPath).toFile().mkdirs()
+
+        def outFileName
+        if (pluginCoverage.isConfig()) {
+            outFileName = "${normalizePluginName(pluginCoverage.getPluginName())}-yang-config-index.adoc"
+        } else {
+            outFileName = "${normalizePluginName(pluginCoverage.getPluginName())}-yang-operational-index.adoc"
+        }
+
+        def outFilePath = Paths.get(outPath, outFileName)
+
+        Files.write(outFilePath, result.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE)
+        log.info "Plugin ${pluginCoverage.getPluginName()} config api docs sucessfully writen to ${outFilePath.toString()}"
+    }
+
+    private static String generateConfigTableContent(final Set<CoverageUnit> coverage) {
+        coverage.stream()
+                .map { unit ->
+            "$NL" +
+                    "${vppApiWithLink(unit.vppApi)}" +
+                    "${javaApi(unit.javaApi)}" +
+                    "${yangTypes(unit.yangTypes)}" +
+                    "${supportedOperations(unit.supportedOperations)}"
+        }
+        .collect(Collectors.joining(NL))
+    }
+
+    private static String vppApiWithLink(final VppApiMessage vppApi) {
+        "$TABLE_PART_MARK${vppApi.link}[${vppApi.name}]$NL"
+    }
+
+    private static String javaApi(final JavaApiMessage javaApi) {
+        "$TABLE_PART_MARK${javaApi.api}$NL"
+    }
+
+    private static String yangTypes(final List<YangType> yangTypes) {
+        "$NL$TABLE_PART_MARK$NL ${yangTypes.stream().map { yangType -> " ${yangType.link}[${yangType.type}]" }.collect(Collectors.joining(NL))}"
+    }
+
+    private static String supportedOperations(final List<Operation> operations) {
+        "$NL$TABLE_PART_MARK${operations.stream().map { reference -> " ${reference.link}[${reference.operation}]" }.collect(Collectors.joining(NL))}"
+    }
+
+    private static String normalizePluginName(final String name) {
+        name.toLowerCase().replace(" ", "-")
+    }
+}
diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockAclModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockAclModule.java
new file mode 100644 (file)
index 0000000..4360618
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core.mock.binding;
+
+import static io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule.noOpProxy;
+
+import com.google.inject.Provider;
+import io.fd.hc2vpp.acl.AclModule;
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade;
+import java.io.IOException;
+
+/**
+ * Use to bypass jvpp registration
+ */
+public class MockAclModule extends AclModule {
+
+    public MockAclModule() {
+        super(MockFutureAclProvider.class);
+    }
+
+    static class MockFutureAclProvider implements Provider<FutureJVppAclFacade> {
+
+        @Override
+        public FutureJVppAclFacade get() {
+            try {
+                return new FutureJVppAclFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class));
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockBindingModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockBindingModule.java
new file mode 100644 (file)
index 0000000..e6500ea
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core.mock.binding;
+
+import static com.google.inject.name.Names.named;
+
+import com.google.inject.AbstractModule;
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+
+/**
+ * Use to bypass jvpp registration, and infra modules
+ */
+public class MockBindingModule extends AbstractModule {
+
+    private static final InvocationHandler NOOP_INVOCATION_HANDLER = (proxy, method, args) -> null;
+
+    @Override
+    protected void configure() {
+        bind(FutureJVppCore.class).toInstance(noOpProxy(FutureJVppCore.class));
+        bind(MappingContext.class).annotatedWith(named("honeycomb-context"))
+                .toInstance(noOpProxy(MappingContext.class));
+        bind(DataBroker.class).annotatedWith(named("honeycomb-context")).toInstance(noOpProxy(DataBroker.class));
+        bind(JVppRegistry.class).toInstance(noOpProxy(JVppRegistry.class));
+    }
+
+    static <T> T noOpProxy(Class<T> clazz) {
+        return (T) Proxy.newProxyInstance(MockBindingModule.class.getClassLoader(),
+                new Class[]{clazz}, NOOP_INVOCATION_HANDLER);
+    }
+}
diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockIoamModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockIoamModule.java
new file mode 100644 (file)
index 0000000..f3f0619
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core.mock.binding;
+
+import static io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule.noOpProxy;
+
+import com.google.inject.Provider;
+import io.fd.hc2vpp.vppioam.impl.VppIoamModule;
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexportFacade;
+import io.fd.vpp.jvpp.ioampot.future.FutureJVppIoampotFacade;
+import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtraceFacade;
+import java.io.IOException;
+
+/**
+ * Use to bypass jvpp registration
+ */
+public class MockIoamModule extends VppIoamModule {
+
+    public MockIoamModule() {
+        super(MockTraceProvider.class, MockPotProvider.class, MockExportProvider.class);
+    }
+
+    private static class MockTraceProvider implements Provider<FutureJVppIoamtraceFacade> {
+        @Override
+        public FutureJVppIoamtraceFacade get() {
+            try {
+                return new FutureJVppIoamtraceFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class));
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+
+    private static class MockPotProvider implements Provider<FutureJVppIoampotFacade> {
+
+        @Override
+        public FutureJVppIoampotFacade get() {
+            try {
+                return new FutureJVppIoampotFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class));
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+
+    private static class MockExportProvider implements Provider<FutureJVppIoamexportFacade> {
+
+        @Override
+        public FutureJVppIoamexportFacade get() {
+            try {
+                return new FutureJVppIoamexportFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class));
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNatModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNatModule.java
new file mode 100644 (file)
index 0000000..d74f7f2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core.mock.binding;
+
+import static io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule.noOpProxy;
+
+import com.google.inject.Provider;
+import io.fd.hc2vpp.nat.NatModule;
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
+import java.io.IOException;
+
+/**
+ * Use to bypass jvpp registration
+ */
+public class MockNatModule extends NatModule {
+
+    public MockNatModule() {
+        super(MockJVppSnatProvider.class);
+    }
+
+    private static class MockJVppSnatProvider implements Provider<FutureJVppSnatFacade> {
+
+        @Override
+        public FutureJVppSnatFacade get() {
+            try {
+                return new FutureJVppSnatFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class));
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNshModule.java b/vpp-integration/api-docs/scripts/src/main/java/io/fd/hc2vpp/docs/core/mock/binding/MockNshModule.java
new file mode 100644 (file)
index 0000000..dace214
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * 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.
+ */
+
+package io.fd.hc2vpp.docs.core.mock.binding;
+
+import static io.fd.hc2vpp.docs.core.mock.binding.MockBindingModule.noOpProxy;
+
+import com.google.inject.Provider;
+import io.fd.hc2vpp.vppnsh.impl.VppNshModule;
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.nsh.future.FutureJVppNshFacade;
+import java.io.IOException;
+
+/**
+ * Use to bypass jvpp registration
+ */
+public class MockNshModule extends VppNshModule {
+
+    public MockNshModule() {
+        super(MockJVppNshProvider.class);
+    }
+
+    private static class MockJVppNshProvider implements Provider<FutureJVppNshFacade> {
+
+        @Override
+        public FutureJVppNshFacade get() {
+            try {
+                return new FutureJVppNshFacade(noOpProxy(JVppRegistry.class), noOpProxy(JVpp.class));
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
+}
diff --git a/vpp-integration/api-docs/scripts/src/main/resources/routing.json b/vpp-integration/api-docs/scripts/src/main/resources/routing.json
new file mode 100644 (file)
index 0000000..95f8541
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "default-routing-instance-name": "vpp-routing-instance",
+  "learned-route-name-prefix": "learned-route"
+}
\ No newline at end of file
diff --git a/vpp-integration/api-docs/scripts/src/main/resources/vpp-management.json b/vpp-integration/api-docs/scripts/src/main/resources/vpp-management.json
new file mode 100644 (file)
index 0000000..28b25b2
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "keepalive-delay":30
+}
\ No newline at end of file
diff --git a/vpp-integration/api-docs/scripts/src/main/resources/yang_to_jvpp_template b/vpp-integration/api-docs/scripts/src/main/resources/yang_to_jvpp_template
new file mode 100644 (file)
index 0000000..cf9502c
--- /dev/null
@@ -0,0 +1,6 @@
+.${pluginName} to Yang index
+[width="80%",options="header",cols="1,1,m,1"]
+|===
+|*VPP api* |*Java api* |*Yang nodes* |*Supported Operations*
+${tableContent}
+|===
\ No newline at end of file
index 5c3da79..1bdd655 100644 (file)
@@ -33,6 +33,7 @@
 
     <modules>
         <module>minimal-distribution</module>
+        <module>api-docs</module>
     </modules>
 
     <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->