blob: 747708bed4f56010da6d7c0bdaafdfd0e94702cb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.felix.maven.osgicheck.impl.mddocgen;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.felix.scr.impl.metadata.ComponentMetadata;
import org.apache.felix.scr.impl.metadata.Faker;
import org.apache.felix.scr.impl.metadata.PropertyMetadata;
import org.apache.felix.scr.impl.metadata.ServiceMetadata;
import org.apache.felix.scr.impl.xml.XmlHandler;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
@Mojo(
name = "generate-services-doc",
defaultPhase = LifecyclePhase.PACKAGE,
threadSafe = false
)
public final class ServicesMarkdownGeneratorMojo extends AbstractMarkdownMojo {
// plugin parameters
@Parameter(defaultValue="${project.build.outputDirectory}")
private File servicesDirectory;
@Parameter(defaultValue="${project.build.directory}/mddoc/services/${project.artifactId}/${project.version}")
private File servicesMarkdownDirectory;
@Parameter(defaultValue="${project.name} ${project.version} Services", readonly = true)
private String readmeTitle;
private final MavenScrLogger mavenScrLogger = new MavenScrLogger(getLog());
@Override
protected String getReadmeTitle() {
return readmeTitle;
}
@Override
protected File getSourceDir() {
return servicesDirectory;
}
@Override
protected File getTargetDir() {
return servicesMarkdownDirectory;
}
@Override
protected String getIncludes() {
return "**/OSGI-INF/*.xml";
}
@Override
protected void handle(Collection<File> services) {
for (File serviceFile : services) {
List<ComponentMetadata> metadata = readComponentMetadata(serviceFile);
for (ComponentMetadata component : metadata) {
if (isExcluded(component.getImplementationClassName())) {
continue;
}
ServiceMetadata serviceMetadata = component.getServiceMetadata();
if (serviceMetadata != null) {
String[] providedServices = serviceMetadata.getProvides();
if (providedServices != null && providedServices.length > 0) {
ClassName className = ClassName.get(component.getImplementationClassName());
// index all services first
for (String providedService : providedServices) {
// index each service impl to the related provided
doIndex(providedService, className);
}
// write related impl metadata
File targetDir = new File(servicesMarkdownDirectory, className.getPackagePath());
if (!targetDir.exists()) {
targetDir.mkdirs();
}
File targetFile = new File(targetDir, className.getSimpleName() + ".md");
try(PrintWriter writer = newPrintWriter(targetFile)) {
// generic properties
writer.format("# %s%n%n", component.getName());
// terrible hack, but there's no other way
List<PropertyMetadata> properties = Faker.getPropertyMetaData(component);
if (!properties.isEmpty()) {
writer.println("# Properties");
writer.println();
writer.println("| Name | Type | Value |");
writer.println("| ---- | ---- | ----- |");
for (PropertyMetadata property : properties) {
writer.format("| %s | %s | %s |%n",
property.getName(),
property.getType() != null ? property.getType() : "String",
property.getValue().getClass().isArray() ? Arrays.toString((Object[]) property.getValue()) : property.getValue());
}
}
} catch (IOException e) {
getLog().error("An error occurred while rendering documentation in " + targetFile, e);
}
}
}
}
}
}
private List<ComponentMetadata> readComponentMetadata(File serviceFile) {
getLog().debug("Analyzing '" + serviceFile + "' SCR file...");
// read the original XML file
List<ComponentMetadata> metadata = null;
try {
XmlHandler xmlHandler = new XmlHandler(new SyntheticBundle(servicesDirectory, serviceFile), mavenScrLogger, false, true, null);
final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
final SAXParser parser = factory.newSAXParser();
parser.parse(serviceFile, xmlHandler);
getLog().debug("SCR file '" + serviceFile + "' successfully load");
metadata = xmlHandler.getComponentMetadataList();
} catch (Exception e) {
getLog().error("SCR file '"
+ serviceFile
+ "' could not be read", e);
metadata = Collections.emptyList();
}
return metadata;
}
}