Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.jboss.forge.roaster.Roaster;
import org.jboss.forge.roaster.model.source.EnumConstantSource;
import org.jboss.forge.roaster.model.source.JavaEnumSource;
import org.jboss.forge.roaster.model.source.JavaInterfaceSource;
import org.jboss.forge.roaster.model.source.JavaSource;
import org.jboss.forge.roaster.model.source.MethodSource;

Expand Down Expand Up @@ -71,6 +72,20 @@ else if (!path.toString().endsWith(".java")) {
}
JavaEnumSource enumSource = (JavaEnumSource) javaSource;

enumSource.getInterfaces().forEach(interfaceName -> {
// Find any interfaces that match our supported interfaces
JavaInterfaceSource foundInterface = searchHelper.searchExtendingDocumentationInterfaceName(interfaceName,
this);
if (foundInterface != null) {
try {
supportedInterfaces().add(Class.forName(interfaceName));
}
catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
});

if (supportedInterfaces().stream().noneMatch(enumSource::hasInterface)) {
return FileVisitResult.CONTINUE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.jboss.forge.roaster.model.source.EnumConstantSource;
import org.jboss.forge.roaster.model.source.Import;
import org.jboss.forge.roaster.model.source.JavaEnumSource;
import org.jboss.forge.roaster.model.source.JavaInterfaceSource;
import org.jboss.forge.roaster.model.source.JavaSource;
import org.jboss.forge.roaster.model.source.MethodHolderSource;
import org.jboss.forge.roaster.model.source.MethodSource;
Expand Down Expand Up @@ -151,6 +152,20 @@ public JavaSource<?> search(String qualifiedName) {
return info.getJavaSource();
}

/**
* Search a {@link JavaInterfaceSource} by qualified class name.
* @param qualifiedName a qualified class name
* @return matched {@link JavaInterfaceSource} or {@code null} if not found.
*/
@Nullable
public JavaInterfaceSource searchInterface(String qualifiedName) {
JavaSource<?> javaSource = search(qualifiedName);
if (javaSource == null || !javaSource.isInterface()) {
return null;
}
return (JavaInterfaceSource) javaSource;
}

/**
* Search the class which is referenced by the enclosing class.
* @param enclosingJavaSource enclosing java class source
Expand Down Expand Up @@ -450,6 +465,61 @@ private JavaSource<?> searchJavaSourceByRoasterTypeName(JavaSource<?> enclosingJ
return null;
}

/**
* Search for interfaces that extend the documentation interfaces.
* @param interfaceName
* @param abstractSearchingFileVisitor
* @return the {@link JavaInterfaceSource} that extends the documentation interface or
* {@code null} if no matching interface is found
*/
@Nullable
public JavaInterfaceSource searchExtendingDocumentationInterfaceName(String interfaceName,
AbstractSearchingFileVisitor abstractSearchingFileVisitor) {
// delegate to recursive search
return searchExtendingDocumentationInterfaceName(interfaceName, new HashSet<>(), abstractSearchingFileVisitor);
}

@Nullable
private JavaInterfaceSource searchExtendingDocumentationInterfaceName(String interfaceName, Set<String> visited,
AbstractSearchingFileVisitor visitor) {
// Search for the interface
JavaInterfaceSource interfaceSource = searchInterface(interfaceName);
if (interfaceSource == null) {
return null;
}

String qualifiedName = interfaceSource.getQualifiedName();
logger.trace("Searching ExtendingDocumentationInterface on {} - start", qualifiedName);

// Cycle detection
if (visited.contains(qualifiedName)) {
return null;
}
visited.add(qualifiedName);

// Check if this interface is supported
if (visitor.supportedInterfaces().stream().anyMatch(name -> name.getName().equals(qualifiedName))) {
return interfaceSource;
}

// Check all extended interfaces
for (String childInterface : interfaceSource.getInterfaces()) {
// Check if child is directly supported
if (visitor.supportedInterfaces().stream().anyMatch(i -> i.getName().equals(childInterface))) {
return interfaceSource;
}

// Recursive search through child
JavaInterfaceSource result = searchExtendingDocumentationInterfaceName(childInterface, visited, visitor);
if (result != null) {
return result;
}
}

// No matching interface found
return null;
}

/**
* Hierarchically search the implementing name of {@link ObservationConvention} or
* {@link GlobalObservationConvention}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,17 @@ class MetricSearchingFileVisitor extends AbstractSearchingFileVisitor {

private final Collection<MetricEntry> entries;

private final Collection<Class<?>> supportedInterfaces = new ArrayList<>(
Arrays.asList(MeterDocumentation.class, ObservationDocumentation.class));
Comment on lines +59 to +60

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this work?

Suggested change
private final Collection<Class<?>> supportedInterfaces = new ArrayList<>(
Arrays.asList(MeterDocumentation.class, ObservationDocumentation.class));
private final Collection<Class<?>> supportedInterfaces = Arrays.asList(MeterDocumentation.class, ObservationDocumentation.class);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not work, as Arrays.asList creates an immutable list, but I'm adding the found extending interfaces to the supportedInterfaces array. See AbstractSearchingFileVisitor.java#L81.

But maybe there is a better way of doing this, which I didn't think of?


MetricSearchingFileVisitor(Pattern pattern, Collection<MetricEntry> entries, JavaSourceSearchHelper searchHelper) {
super(pattern, searchHelper);
this.entries = entries;
}

@Override
public Collection<Class<?>> supportedInterfaces() {
return Arrays.asList(MeterDocumentation.class, ObservationDocumentation.class);
return supportedInterfaces;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class SpanSearchingFileVisitor extends AbstractSearchingFileVisitor {

private final Collection<SpanEntry> spanEntries;

private final Collection<Class<?>> supportedInterfaces = new ArrayList<>(
Arrays.asList(SpanDocumentation.class, ObservationDocumentation.class));
Comment on lines +59 to +60

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this work?

Suggested change
private final Collection<Class<?>> supportedInterfaces = new ArrayList<>(
Arrays.asList(SpanDocumentation.class, ObservationDocumentation.class));
private final Collection<Class<?>> supportedInterfaces = Arrays.asList(SpanDocumentation.class, ObservationDocumentation.class);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not work, as Arrays.asList creates an immutable list, but I'm adding the found extending interfaces to the supportedInterfaces array. See AbstractSearchingFileVisitor.java#L81.

But maybe there is a better way of doing this, which I didn't think of?


/**
* The enclosing enum classes for overriding will be excluded from documentation
*/
Expand All @@ -68,7 +71,7 @@ class SpanSearchingFileVisitor extends AbstractSearchingFileVisitor {

@Override
public Collection<Class<?>> supportedInterfaces() {
return Arrays.asList(SpanDocumentation.class, ObservationDocumentation.class);
return supportedInterfaces;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright 2025 the original author or authors.
*
* 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
*
* https://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.micrometer.docs.metrics.test2;

import java.io.Serializable;

// Added any interface (Serializable) to test if the parser really goes through all extending interfaces
public interface ExtendExtendedMeterDoc extends Serializable, ExtendMeterDocumentation {

String secondDescription();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright 2025 the original author or authors.
*
* 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
*
* https://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.micrometer.docs.metrics.test2;

import io.micrometer.core.instrument.Meter;

public enum ExtendExtendedMyMetrics implements ExtendExtendedMeterDoc {

/**
* Test metric which extends the extended MeterDocumentation interface.
*/
EXTEND_EXTENDED_FOO {
@Override
public String secondDescription() {
return "This comes from ExtendedExtendMeterDoc interface";
}

@Override
public String getDescription() {
return "This comes from ExtendMeterDocumentation interface";
}

@Override
public String getName() {
return "extend.extended.foo";
}

@Override
public Meter.Type getType() {
return Meter.Type.COUNTER;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright 2025 the original author or authors.
*
* 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
*
* https://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.micrometer.docs.metrics.test2;

import io.micrometer.core.instrument.docs.MeterDocumentation;

public interface ExtendMeterDocumentation extends MeterDocumentation {

/**
* Returns the description (also known as {@code help} in some systems) for the given
* meter.
*/
String getDescription();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright 2025 the original author or authors.
*
* 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
*
* https://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.micrometer.docs.metrics.test2;

import io.micrometer.core.instrument.Meter;

public enum ExtendedMyMetrics implements ExtendMeterDocumentation {

/**
* Test metric which extends the MeterDocumentation interface
*/
FOO {
@Override
public String getDescription() {
return "foo metric";
}

@Override
public String getName() {
return "extended.foo";
}

@Override
public Meter.Type getType() {
return Meter.Type.COUNTER;
}
};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright 2025 the original author or authors.
*
* 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
*
* https://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.micrometer.docs.metrics.test2;

import io.micrometer.docs.metrics.MetricsDocGenerator;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Pattern;

class MetricSearchingFileVisitorTest {

@Test
void should_render_metrics_from_sub_class_interfaces() throws IOException {
Path output = Paths.get(".", "build", "_metrics.adoc");

File sourceRoot = new File(".", "src/test");
new MetricsDocGenerator(sourceRoot, Pattern.compile(".*/docs/metrics/test2/[a-zA-Z]+\\.java"),
"templates/metrics.adoc.hbs", output)
.generate();

BDDAssertions.then(new String(Files.readAllBytes(output)))
.contains("**Metric name** `extended.foo`. **Type** `counter`")
.contains("**Metric name** `extend.extended.foo`. **Type** `counter`");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright 2025 the original author or authors.
*
* 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
*
* https://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.micrometer.docs.spans.test7;

public enum ExtendExtendedMySpan implements ExtendExtendedSpanDocumentation {

/**
* Test span for sub-classing documentation interfaces
*/
EXTEND_EXTENDED_FOO {
@Override
public String secondDescription() {
return "This comes from ExtendExtendedSpanDocumentation";
}

@Override
public String getDescription() {
return "This comes from ExtendSpanDocumentation";
}

@Override
public String getName() {
return "extend extended foo";
}
}

}
Loading