SquirrelJME/SquirrelJME

View on GitHub
tools/markdown-javadoc/src/main/java/cc/squirreljme/doclet/ProcessedClass.java

Summary

Maintainability
A
40 mins
Test Coverage
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// Multi-Phasic Applications: SquirrelJME
//     Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------

package cc.squirreljme.doclet;

import cc.squirreljme.runtime.cldc.util.ReferenceList;
import cc.squirreljme.runtime.cldc.util.SortedTreeMap;
import com.sun.javadoc.ClassDoc;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.multiphasicapps.classfile.ClassName;
import net.multiphasicapps.classfile.FieldNameAndType;
import net.multiphasicapps.classfile.MethodNameAndType;
import net.multiphasicapps.collections.UnmodifiableList;
import net.multiphasicapps.markdownwriter.MarkdownWriter;

/**
 * Represents a single processed clased within a file.
 *
 * @since 2022/08/24
 */
public final class ProcessedClass
{
    /** The name of this class. */
    protected final ClassName name;
    
    /** The class document used. */
    protected final ClassDoc classDoc;
    
    /** The doclet this is a part of. */
    private final Reference<MarkdownDoclet> _doclet;
    
    /** The fields available in the class. */
    private final Map<FieldNameAndType, ProcessedField> _fields =
        new SortedTreeMap<>();
    
    /** The methods available in the class. */
    private final Map<MethodNameAndType, ProcessedMethod> _methods =
        new SortedTreeMap<>();
    
    /** Is this a visible class? */
    volatile boolean _isVisible;
    
    /** The class that is the parent of this one. */
    private volatile Reference<ProcessedClass> _parentClass;
    
    /** Classes which are a children of this one. */
    private volatile ReferenceList<ProcessedClass> _childrenClass;
    
    /** The super class. */
    private volatile Reference<ProcessedClass> _superClass;
    
    /** Interface classes. */
    private volatile ReferenceList<ProcessedClass> _interfaceClasses;
    
    /** Where is this document located? */
    volatile Path _documentPath;
    
    /** Is this class implicit, as in part of this package? */
    volatile boolean _implicit;
    
    /** The class description. */
    private volatile String _description;
    
    /** Did we do stage one? */
    private volatile boolean _didStageOne;
    
    /**
     * Processes the given class.
     * 
     * @param __doclet Doclet state.
     * @param __name The name of the class.
     * @param __classDoc The class document.
     * @throws NullPointerException On null arguments.
     * @since 2022/08/24
     */
    public ProcessedClass(MarkdownDoclet __doclet, ClassName __name,
        ClassDoc __classDoc)
        throws NullPointerException
    {
        if (__name == null)
            throw new NullPointerException("NARG");
        
        this._doclet = new WeakReference<>(__doclet);
        this.name = __name;
        this.classDoc = __classDoc;
    }
    
    /**
     * Returns the class description.
     * 
     * @since 2022/08/24
     */
    public String description()
    {
        String description = this._description;
        
        if (description == null || description.trim().isEmpty())
            return "NOT DOCUMENTED";
        
        return description;
    }
    
    /**
     * Returns the children classes which this contains.
     * 
     * @return The children classes.
     * @since 2022/08/27
     */
    public List<ProcessedClass> childrenClasses()
    {
        ReferenceList<ProcessedClass> children = this._childrenClass;
        
        if (children == null)
            return null;
        
        return UnmodifiableList.of(children);
    }
    
    /**
     * Returns the doclet this is in.
     * 
     * @return The doclet this is in.
     * @since 2022/08/24
     */
    public MarkdownDoclet doclet()
    {
        MarkdownDoclet result = this._doclet.get();
        if (result == null)
            throw new Error("GCGC");
        return result;
    }
    
    /**
     * Returns the interface classes which are used.
     * 
     * @return The interface classes.
     * @since 2022/08/27
     */
    public List<ProcessedClass> interfaceClasses()
    {
        ReferenceList<ProcessedClass> interfaces = this._interfaceClasses;
        
        if (interfaces == null)
            return null;
        
        return UnmodifiableList.of(interfaces);
    }
    
    /**
     * Returns the parent of this class.
     * 
     * @return The parent class.
     * @since 2022/08/24
     */
    public ProcessedClass parentClass()
    {
        Reference<ProcessedClass> ref = this._parentClass;
        if (ref == null)
            return null;
            
        ProcessedClass parent = ref.get();
        if (parent == null)
            throw new Error("GCGC");
        return parent;
    }
    
    /**
     * Performs first stage processing.
     * 
     * @since 2022/08/24
     */
    public void stageOne()
    {
        // Only perform stage one, once
        if (this._didStageOne)
            return;
        this._didStageOne = true;
        
        ClassDoc classDoc = this.classDoc;
        MarkdownDoclet doclet = this.doclet();
        
        // Is this a visible class to the JavaDoc?
        this._isVisible = classDoc.isPublic() || classDoc.isProtected();
        
        // Is there a description?
        this._description = Utilities.neatText(classDoc.commentText());
        
        // Determine classes this is a parent of
        ClassDoc[] innerClasses = classDoc.innerClasses(true);
        if (innerClasses != null)
        {
            ReferenceList<ProcessedClass> childrenClasses = ReferenceList.of(
                new ArrayList<>(innerClasses.length));
            
            // Link together children and parent
            for (ClassDoc innerClass : innerClasses)
            {
                ProcessedClass child = doclet.processClass(innerClass);
                
                // Link the two
                child._parentClass = new WeakReference<>(this);
                childrenClasses.add(child);
            }
            
            // Store known children
            this._childrenClass = childrenClasses;
        }
        
        // Super class?
        ClassDoc superClass = classDoc.superclass();
        if (superClass != null)
            this._superClass = new WeakReference<>(
                doclet.processClass(superClass));
        
        // Enums do not actually extend off anything in JavaDoc, so make sure
        // every enum does extend Enum
        else if (classDoc.isEnum())
            this._superClass = new WeakReference<>(
                doclet.processClass(
                    doclet.rootDoc.classNamed("java.lang.Enum")));
        
        // Interface classes
        ClassDoc[] interfaces = classDoc.interfaces();
        if (interfaces != null && interfaces.length > 0)
        {
            ReferenceList<ProcessedClass> result = ReferenceList.of(
                new ArrayList<>(interfaces.length));
            
            for (ClassDoc classy : interfaces)
                result.add(doclet.processClass(classy));
            
            this._interfaceClasses = result;
        }
    }
    
    /**
     * Returns the superclass of this class.
     * 
     * @return The super class.
     * @since 2022/08/27
     */
    public ProcessedClass superClass()
    {
        Reference<ProcessedClass> ref = this._superClass;
        if (ref == null)
            return null;
            
        ProcessedClass superClass = ref.get();
        if (superClass == null)
            throw new Error("GCGC");
        return superClass;
    }
    
    /**
     * Writes the document to the output.
     * 
     * @param __writer The writer to write to.
     * @throws IOException On write errors.
     * @throws NullPointerException On null arguments.
     * @since 2022/08/24
     */
    public void write(MarkdownWriter __writer)
        throws IOException, NullPointerException
    {
        if (__writer == null)
            throw new NullPointerException("NARG");
        
        // The name of the class
        __writer.header(true, 1, this.name.toRuntimeString());
        
        // For the next group...
        ProcessedClass parent = this.parentClass();
        ProcessedClass superClass = this.superClass();
        List<ProcessedClass> interfaceClasses = this.interfaceClasses();
        List<ProcessedClass> childrenClasses = this.childrenClasses();
        
        // Super class, interfaces, and such
        if (superClass != null || parent != null ||
            (interfaceClasses != null && !interfaceClasses.isEmpty()) ||
            (childrenClasses != null && !childrenClasses.isEmpty()))
        {
            __writer.listStart();
            
            // Super class?
            if (superClass != null)
            {
                __writer.append(true, "Super: ");
                Utilities.writerLinkTo(__writer, this, superClass);
                
                __writer.listNext();
            }
            
            // Parent class?
            if (parent != null)
            {
                __writer.append(true, "Parent: ");
                Utilities.writerLinkTo(__writer, this, parent);
                
                __writer.listNext();
            }
            
            // Interfaces?
            if (interfaceClasses != null && !interfaceClasses.isEmpty())
            {
                // Note it
                __writer.append(true, "Interfaces:");
                
                // Stop listing
                this.__writeClassList(__writer, interfaceClasses);
            }
            
            // Children
            if (childrenClasses != null && !childrenClasses.isEmpty())
            {
                // Note it
                __writer.append(true, "Children:");
                
                this.__writeClassList(__writer, childrenClasses);
            }
            
            // End 
            __writer.listEnd();
            
            // Make sure paragraph follows
            __writer.paragraph();
        }
        
        // Write description of the class
        __writer.print(true, this.description());
    }
    
    /**
     * Writes a list of classes.
     * 
     * @param __writer The writer to write to.
     * @param __classes The classes to write.
     * @throws IOException On write errors.
     * @since 2022/08/27
     */
    private void __writeClassList(MarkdownWriter __writer,
        List<ProcessedClass> __classes)
        throws IOException
    {
        // Stop listing
        __writer.listStart();
        
        // Write them all out
        for (ProcessedClass classy : __classes)
        {
            Utilities.writerLinkTo(__writer, this, classy);
            __writer.listNext();
        }
        
        // End list
        __writer.listEnd();
    }
}