class AutoJavadoc<T extends HasJavadoc<?>> implements Consumer<T> {
    
	private static final String DEFAULT_TEXT = "Write some documentation here.";
	private static final String DEFAULT_NAME = "Your Name";
 
    /**
     * Parses the specified model recursively to find all models that implements
     * the {@link HasJavadoc} trait but that does not have proper documentation
     * and generates javadoc stubs for those models.
     * 
     * @param model  the model to parse
     */
    @Override
    public void accept(T model) {
		createJavadoc(requireNonNull(model));
    }
	
    /**
     * Checks if the specified model already has documentation and if not,
     * generates it. This method will recurse through the model tree to make
     * sure all children also have documentation.
     * <p>
     * If documentation exists but is incomplete, it will be completed without
     * changing the existing content.
     * 
     * @param <T>    the type of the model to operate on
     * @param model  the model to add documentation to 
     */
    @SuppressWarnings("unchecked")
	private static <T extends HasJavadoc<?>> void createJavadoc(T model) {<FILL_FUNCTION_BODY>}
	
    /**
     * Add a javadoc tag to the specified documentation block. If the tag is
     * already defined, this will have no effect.
     * 
     * @param doc  the documentation block
     * @param tag  the tag to add
     */
	private static void addTag(Javadoc doc, JavadocTag tag) {
		if (!hasTagAlready(
            requireNonNull(doc), 
            requireNonNull(tag)
        )) {
			doc.add(tag);
		}
	}
	
    /**
     * Checks if the specified tag is already defined in the supplied
     * documentation block. 
     * 
     * @param doc  the documentation block
     * @param tag  the tag to check
     * @return     {@code true} if it exists, else {@code false}
     */
	private static boolean hasTagAlready(Javadoc doc, JavadocTag tag) {
        requireNonNull(doc);
        requireNonNull(tag);
        
		return doc.getTags().stream().anyMatch(t -> 
			tag.getName().equals(t.getName())
			&& tag.getValue().equals(t.getValue())
		);
	}
}

        
		final Javadoc doc = requireNonNull(model).getJavadoc().orElse(Javadoc.of(DEFAULT_TEXT));
		model.set(doc);

		if (model instanceof HasGenerics) {
            // Add @param for each type variable.
			((HasGenerics<?>) model).getGenerics().forEach(g -> 
				g.getLowerBound().ifPresent(t -> addTag(doc, 
					PARAM.setValue("<" + t + ">")
				))
			);
		}

		if (model instanceof ClassOrInterface) {
			// Add @author
			doc.add(AUTHOR.setValue(DEFAULT_NAME));
            
		} else {
			// Add @param for each parameter.
			if (model instanceof HasFields) {
				((HasFields<?>) model).getFields().forEach(f -> 
					addTag(doc, PARAM.setValue(f.getName()))
				);
			}
		}

        if ((model instanceof Method) && (((Method) model).getType() != void.class)) {
            // Add @return to methods.
            addTag(doc, RETURN);
        }
		
		
		if (model instanceof HasConstructors) {
            // Generate javadoc for each constructor.
			((HasConstructors<?>) model).getConstructors()
				.forEach(AutoJavadoc::createJavadoc);
		}

		if (model instanceof HasMethods) {
            // Generate javadoc for each method.
			((HasMethods<?>) model).getMethods()
				.forEach(AutoJavadoc::createJavadoc);
		}
        
        if (model instanceof HasClasses) {
            // Generate javadoc for each subclass.
            ((HasClasses<?>) model).getClasses()
                .forEach(AutoJavadoc::createJavadoc);
        }
	
<no_super_class>