com.vecna.maven.hibernate.HibernateDocMojo Maven / Gradle / Ivy
/**
* Copyright 2011 Vecna Technologies, Inc.
*
* 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 com.vecna.maven.hibernate;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.codehaus.plexus.util.FileUtils;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.property.Getter;
import org.hibernate.tool.hbm2x.DocExporter;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.Type;
/**
* Generates schema documentation. This plugin enhances the Hibernate model with documentation extracted from the javadocs
* (sources must be provided). Then it feeds the Hibernate model into the Hibernate Tools DocExporter.
* @author [email protected]
*/
@Mojo(name = "doc",
defaultPhase = LifecyclePhase.COMPILE,
requiresDependencyResolution = ResolutionScope.RUNTIME,
threadSafe = true)
public class HibernateDocMojo extends HibernateSchemaMojo {
/**
* Output directory for schema documentation.
*/
@Parameter
private File outputDir;
/**
* Directories that contain the source code for persistent classes.
*/
@Parameter
private File[] sourceDirs = new File[0];
/**
* A regex for figuring out which columns are encrypted (the full name of the Hibernate type must match this).
*/
@Parameter
private String encryptedTypeRegex;
/**
* @return build javadocs from the source locations
*/
private JavaDocBuilder findJavadocs() {
JavaDocBuilder builder = new JavaDocBuilder();
for (File sourceDir : sourceDirs) {
if (!sourceDir.exists() || !sourceDir.isDirectory()) {
getLog().error("Invalid source directory: " + sourceDir);
} else {
builder.addSourceTree(sourceDir);
}
}
return builder;
}
/**
* @return javadoc types from java types (for looking up method javadocs by signature)
* @param classes java types
*/
private Type[] getJavaDocTypes(Class>[] classes) {
com.thoughtworks.qdox.model.Type[] types = new com.thoughtworks.qdox.model.Type[classes.length];
for (int i = 0; i < types.length; i++) {
types[i] = new com.thoughtworks.qdox.model.Type(classes[i].getName());
}
return types;
}
/**
* @return javadocs for a simple Hibernate property
* @param property hibernate property
* @param cls the class that owns the property
* @param javaClass javadoc model for the class
*/
private String getSimpleValueJavadoc(Property property, Class> cls, JavaClass javaClass) {
Getter getter = property.getGetter(cls);
Member member = getter.getMember();
if (member instanceof Field) {
JavaField field = javaClass.getFieldByName(member.getName());
if (field != null) {
return field.getComment();
}
} else if (member instanceof Method) {
Method method = (Method) member;
Type[] types = getJavaDocTypes(method.getParameterTypes());
JavaMethod javaMethod = javaClass.getMethodBySignature(method.getName(), types, true);
if (javaMethod == null) {
getLog().warn("can't find java method docs for " + javaClass.getName()
+ " . " + method.getName() + " " + Arrays.asList(types));
} else {
DocletTag tag = javaMethod.getTagByName("return", true);
if (tag != null && tag.getValue() != null) {
return tag.getValue();
}
}
}
return null;
}
/**
* set a comment on Hibernate columns.
* @param comment the comment to set.
* @param columnIterator hibernate column iterator.
*/
private void setComment(String comment, Iterator columnIterator) {
while (columnIterator.hasNext()) {
Column column = columnIterator.next();
if (encryptedTypeRegex != null && column.getValue() instanceof SimpleValue) {
String typeName = ((SimpleValue) column.getValue()).getTypeName();
if (typeName != null && typeName.matches(encryptedTypeRegex)) {
comment += " [encrypted]";
}
}
column.setComment(comment);
}
}
/**
* set a comment on Hibernate columns mapped to a property
* @param comment the comment to set
* @param prop hibernate property
*/
private void setComment(String comment, Property prop) {
@SuppressWarnings("unchecked") Iterator columnIterator = prop.getColumnIterator();
setComment(comment, columnIterator);
}
/**
* concatenate javadoc comments for nested properties
* @param comment the comment to add to the javadoc
* @param accumulatedJavadoc the comment for the parent property
* @return combined comment
*/
private String accumulateJavadoc(String comment, String accumulatedJavadoc) {
if (comment == null) {
comment = "???";
}
if (accumulatedJavadoc != null) {
comment = accumulatedJavadoc + " - " + comment;
}
return comment;
}
/**
* Populate Hibernate properties with comments from javadocs (including nested properties).
* @param propertyIterator iterator over top-level properties
* @param accumulatedJavadoc comments accumulated so far (for nested properties)
* @param cls the class to introspect.
* @param javaDocs javadocs for all classes.
*/
private void processProperties(Iterator propertyIterator, Class> cls,
JavaDocBuilder javaDocs, String accumulatedJavadoc) {
JavaClass javaClass = javaDocs.getClassByName(cls.getName());
if (javaClass != null) {
while (propertyIterator.hasNext()) {
Property prop = propertyIterator.next();
Value value = prop.getValue();
if (value instanceof Collection) {
Collection collection = (Collection) value;
Value elementValue = collection.getElement();
if (elementValue instanceof Component) {
processComponent((Component) elementValue, javaDocs, accumulatedJavadoc);
}
Table collectionTable = collection.getCollectionTable();
if (collectionTable.getComment() == null) {
collectionTable.setComment(getSimpleValueJavadoc(prop, cls, javaClass));
}
} else if (value instanceof Component) {
String comment = getSimpleValueJavadoc(prop, cls, javaClass);
comment = accumulateJavadoc(comment, accumulatedJavadoc);
processComponent((Component) value, javaDocs, comment);
} else if (value instanceof SimpleValue) {
String comment = getSimpleValueJavadoc(prop, cls, javaClass);
comment = accumulateJavadoc(comment, accumulatedJavadoc);
setComment(comment, prop);
}
}
}
}
/**
* Process a component (embedded property) and populate its properties (including nested ones) with javadoc comments.
* @param component component model
* @param javaDocs javadocs
* @param accumulatedJavadoc comments accumulated so far (for nested components)
*/
private void processComponent(Component component, JavaDocBuilder javaDocs, String accumulatedJavadoc) {
@SuppressWarnings("unchecked") Iterator propertyIterator = component.getPropertyIterator();
processProperties(propertyIterator, component.getComponentClass(), javaDocs, accumulatedJavadoc);
}
/**
* Populate table/column comments in a Hibernate model from javadocs
* @param configuration hibernate configuration.
* @param javaDocs javadoc model for all classes.
*/
private void populateCommentsFromJavadocs(Configuration configuration, JavaDocBuilder javaDocs) {
Iterator mappedClasses = configuration.getClassMappings();
while (mappedClasses.hasNext()) {
PersistentClass mappedClass = mappedClasses.next();
Table table = mappedClass.getTable();
JavaClass javaClass = javaDocs.getClassByName(mappedClass.getClassName());
if (javaClass != null) {
if (table != null) {
String comment = javaClass.getComment();
if (mappedClass.getDiscriminator() != null) {
String newComment = "Discriminator '" + mappedClass.getDiscriminatorValue() + "': " + comment;
if (table.getComment() != null) {
newComment = table.getComment() + "
" + newComment;
}
table.setComment(newComment);
@SuppressWarnings("unchecked")
Iterator discriminatorColumns = mappedClass.getDiscriminator().getColumnIterator();
setComment("discriminator - see table comment", discriminatorColumns);
} else {
table.setComment(comment);
}
}
@SuppressWarnings("unchecked") Iterator propertyIterator = mappedClass.getPropertyIterator();
processProperties(propertyIterator, mappedClass.getMappedClass(), javaDocs, null);
}
if (mappedClass.getIdentifierProperty() != null) {
setComment("Primary key", mappedClass.getIdentifierProperty());
}
}
}
@Override
protected void executeWithMappings(Configuration configuration) throws MojoExecutionException, MojoFailureException {
populateCommentsFromJavadocs(configuration, findJavadocs());
try {
FileUtils.forceMkdir(outputDir);
} catch (IOException e) {
throw new MojoExecutionException("cannot create output directory " + outputDir, e);
}
new DocExporter(configuration, outputDir).start();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy