001package Torello.JavaDoc;
002
003
004// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
005// Standard-Java Imports
006// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
007
008import java.util.concurrent.locks.*;
009
010import java.io.IOException;
011import java.util.Optional;
012import java.util.List;
013import java.util.function.Consumer;
014
015
016// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
017// Java-HTML Imports
018// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
019
020import Torello.Java.*;
021
022import static Torello.Java.C.*;
023import static Torello.JavaDoc.PF.*;
024
025import Torello.Java.Additional.Ret2;
026import Torello.Java.Additional.Ret3;
027
028import Torello.Java.ReadOnly.ReadOnlyList;
029import Torello.Java.ReadOnly.ReadOnlyArrayList;
030
031import Torello.JDUInternal.ParseJavaSource.PJSHelper;
032
033
034// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
035// The new Source-Code Parser: com.sun.source.*
036// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
037
038import com.sun.source.tree.ClassTree;
039import com.sun.source.tree.TypeParameterTree;
040import com.sun.source.tree.Tree;
041
042
043/**
044 * <B STYLE='color:darkred;'>Reflection Class:</B>
045 * 
046 * Holds all information extracted from <CODE><B>'&#46;java'</CODE></B> Source-Files regarding
047 * Nested-Types (Inner-Classes).
048 * 
049 * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_GET_INST_2>
050 * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_NESTED>
051 * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_DIAGRAM>
052 */
053public class NestedType extends Declaration implements Cloneable
054{
055    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
056    protected static final long serialVersionUID = 1;
057
058    @Override
059    String codeHiLiteString() { return null; }
060
061
062    // ********************************************************************************************
063    // ********************************************************************************************
064    // Public Fields
065    // ********************************************************************************************
066    // ********************************************************************************************
067
068
069    /** Identifies whether this is a nested/inner Class, Interface, Enum, Annotation etc... */
070    public final CIET ciet;
071
072    /**
073     * The name of the package in which the enclosing-class of this nested-type is defined.  This
074     * field will be null if the package was not found, or left blank as the 'default class'.
075     */
076    public final String packageName;
077
078    /**
079     * The {@code name}-field ({@link Declaration#name}) of this class uses the lone
080     * Java-Identifier (Just One Word) which identifies this {@code NestedClass} within the scope
081     * of the enclosing class.  However, {@code nameWithContainer} is a {@code String} that also
082     * includes any / all enclosing-class names, each followed-by a {@code '.'}
083     * 
084     * <BR /><BR /><B STYLE='color: red;'>EXAMPLE:</B> For Java {@code java.util.Map.Entry<K, V>},
085     * the {@code nameWithContainer} would be {@code "Map.Entry"}.
086     * 
087     * <BR /><BR />Generic Type-Parameter information is <B><I>not</I></B> included in this 
088     * {@code String}, and neither is the Package-Name.
089     */
090    public final String nameWithContainer;
091
092    /**
093     * This field is identical to {@link #nameWithContainer}, but also has the Package-Name
094     * prepended to it, if the Package-Name was present in the enclosing class' {@code '.java'}
095     * file.
096     * 
097     * <BR /><BR /><B STYLE='color: red;'>EXAMPLE:</B> For Java {@code java.util.Map.Entry<K, V>},
098     * the {@code fullyQualifiedName} would be {@code "java.util.Map.Entry"}.
099     */
100    public final String fullyQualifiedName;
101
102    /** The number of fields that are defined in this inner-type */
103    public final int numFields;
104
105    /** The number of methods defined in this inner-type */
106    public final int numMethods;
107
108    /** <EMBED CLASS='external-html' DATA-FILE-ID=NT_GTPARAMS> */
109    public final ReadOnlyList<String> genericTypeParameters;
110
111    /**
112     * <EMBED CLASS='external-html' DATA-FILE-ID=NT_IMPL_TYPES>
113     * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_JOW_TITLE>
114     */
115    public final ReadOnlyList<String> implementedTypesJOW;
116
117    /** <EMBED CLASS='external-html' DATA-FILE-ID=NT_EXTEND_TYPES> */
118    public final ReadOnlyList<String> extendedTypesJOW;
119
120
121    // ********************************************************************************************
122    // ********************************************************************************************
123    // Native Parser Library Hook
124    // ********************************************************************************************
125    // ********************************************************************************************
126
127
128    /**
129     * <EMBED CLASS='external-html' DATA-FILE-ID=SSTB_HOOK_FIELD>
130     * 
131     * If a user decides to make use of the native Sun/Oracle {@code ClassTree} instance that was
132     * used to build this {@code NestedType} instance, it may be retrieved from this
133     * {@code transient} field.
134     */
135    public final transient ClassTree classTree;
136
137
138    // ********************************************************************************************
139    // ********************************************************************************************
140    // Constructor - com.sun.source.tree 
141    // ********************************************************************************************
142    // ********************************************************************************************
143
144
145    /**
146     * <EMBED CLASS="defs" DATA-KIND=NestedType DATA-ENTITY=ClassTree>
147     * <EMBED CLASS='external-html' DATA-FILE-ID=RC_DESCRIPTION>
148     * @param ct <EMBED CLASS='external-html' DATA-FILE-ID=RC_PARAM_TREE>
149     * @param util <EMBED CLASS='external-html' DATA-FILE-ID=RC_PARAM_UTIL>
150     */
151    public NestedType(ClassTree ct, TreeUtils util)
152    {
153        super(
154            util,                           // TreeUtils
155            ct,                             // com.sun.source.Tree.ClassTree instance
156            ct.getModifiers(),              // Annotations **AND** public, static, final
157            ct.getSimpleName().toString(),  // Name of the class    - NEED TO DEBUG THIS !!!
158            Entity.INNER_CLASS,
159            null                            // BODY SHOULDN'T BE NULL, BUT IT IS FOR NOW!!!
160        );
161
162
163        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
164        // Generic Type Parametes
165        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
166
167        // List<? extends TypeParameterTree> genericTypeParams = ct.getTypeParameters();
168        @SuppressWarnings("unchecked")
169        List<TypeParameterTree> genericTypeParams =
170            (List<TypeParameterTree>) ct.getTypeParameters();
171
172        if ((genericTypeParams == null) || (genericTypeParams.size() > 0))
173            this.genericTypeParameters = EMPTY_READONLY_LIST;
174
175        else this.genericTypeParameters = new ReadOnlyArrayList<String>(
176            genericTypeParams,
177            (TypeParameterTree tpt) -> tpt.toString().trim(),
178            genericTypeParams.size()
179        );
180
181
182        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
183        // Implements Clause
184        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
185
186        // List<? extends Tree> implementedTypes = ct.getImplementsClause();
187        @SuppressWarnings("unchecked")
188        List<Tree> implementedTypes = (List<Tree>) ct.getImplementsClause();
189
190        if ((implementedTypes == null) || (implementedTypes.size() == 0))
191            this.implementedTypesJOW = EMPTY_READONLY_LIST;
192
193        else this.implementedTypesJOW = new ReadOnlyArrayList<String>(
194            implementedTypes,
195            (Tree t) -> StrSource.typeToJavaIdentifier(t.toString().trim()).trim(),
196            implementedTypes.size()
197        );
198
199
200        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
201        // Extends Clause
202        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
203
204        // this.extendedTypesJOW = PJSHelper.getExtendedTypes(ct);
205        Tree t = ct.getExtendsClause();
206
207        this.extendedTypesJOW = (t == null)
208            ? EMPTY_READONLY_LIST
209            : ReadOnlyList.of(t.toString());
210
211
212        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
213        // Other Stuff
214        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
215
216        // Reference Hook: This was built using the com.sun.source.tree.ClassTree class, so
217        // there simply isn't a com.github.javaparser.ast.body.TypeDeclaration (so it is
218        // set to null)
219
220        this.classTree = ct;
221
222        // This is the kind of this inner-class / nested-type.  This CIET is computed using the
223        // internal-helper method because it is just a big switch-statement.  I suspect it is going
224        // to be reused elsewhere, but I am not 100% on that yet.
225
226        // this.ciet = PJSHelper.getCIET(ct);
227        switch (ct.getKind())
228        {
229            case CLASS:             this.ciet = CIET.CLASS;         break;
230            case INTERFACE:         this.ciet = CIET.INTERFACE;     break;
231            case ENUM:              this.ciet = CIET.ENUM;          break;
232            case ANNOTATION_TYPE:   this.ciet =  CIET.ANNOTATION;   break;
233
234            default: throw new UnreachableError();
235        }
236
237        /*
238        if (this.ciet == null)
239            Messager.assertFailJavaParser("Unknown Type Declaration: ", this.signature);
240        */
241
242        // This Helper Computes all three of these fields.
243        Ret3<String, String, String> names = PJSHelper.getInnerTypeNames(ct, util);
244
245        // return new Ret3<>(packageName, nameWithContainer, fullyQualifiedName);
246        this.packageName        = names.a;
247        this.nameWithContainer  = names.b;
248        this.fullyQualifiedName = names.c;
249
250        // This Helper counts the number of methods and fields in a TypeDeclaration
251        Ret2<Integer, Integer> counts = PJSHelper.countMethodsFields(ct, this.ciet);
252
253        // return new Ret<>(numMethods, numFields);
254        this.numMethods = counts.a;
255        this.numFields  = counts.b;
256    }
257
258
259    // *************************************************************************************
260    // *************************************************************************************
261    // toString()
262    // *************************************************************************************
263    // *************************************************************************************
264
265
266    /**
267     * Generates a {@code String} of this {@code nested-type}, with most information included.
268     * 
269     * @return A printable {@code String} of this {@code nested-type}.
270     * 
271     * @see PF
272     * @see #toString(int)
273     * @see StrCSV#toCSV(String[], boolean, boolean, Integer)
274     */
275    public String toString()
276    {
277        return
278            "Name:            [" + name + "]\n" +
279            "With Container:  [" + nameWithContainer + "]\n" +
280            "Kind:            [" + ciet.toString() + "]\n" +
281            "Signature:       [" +
282                StrPrint.abbrevEndRDSF(signature, MAX_STR_LEN, true) + "]\n" +
283
284            // "Type Parameters:".length() ==>  16  (17=16+1)
285            printedTypeParameters(17) +
286
287            "Modifiers:       [" +
288                StrCSV.toCSV(modifiers, true, true, null) + "]\n" +
289
290                // This will **NEVER** be null - unless 'this' instance was built from an HTML File,
291                // rather than a source-code file.  Instances like that are only used temporarily, and
292                // are garbage collected instantly.  Do this check anyway (just in case).
293    
294            "Location:        " + ((this.location == null)
295                    ? "null" 
296                    : ('[' + this.location.quickSummary() + ']'));
297    }
298
299    /**
300     * This class expects a flags that has been masked using the constant ({@code public, static,
301     * final int}) fields of class {@link PF}.  Please view this class for more information about
302     * the flags available for modifying the return-value of this {@code toString()} method.
303     * 
304     * @param flags These are the toString flags from class PF ("Print Flags").  View available
305     * flags listed in class {@link PF}.
306     * 
307     * @return A printable {@code String} of this method, with comment information included as well.
308     * 
309     * @see StrCSV#toCSV(String[], boolean, boolean, Integer)
310     * @see #toString()
311     * @see PF
312     */
313    public String toString(int flags)
314    {
315        boolean color = (flags & UNIX_COLORS) > 0;
316        String nameTitle = "Nested " + ciet.toString();
317
318        // 7 ==> " Name: ".length()
319        int LEN = nameTitle.length() + 7;
320
321        return 
322            printedName(nameTitle, LEN, color) +
323            StringParse.rightSpacePad("Fully Qualified:", LEN) + '[' + fullyQualifiedName + "]\n" +
324            StringParse.rightSpacePad("With Container:", LEN) + '[' + nameWithContainer + "]\n" +
325            printedSignature(LEN, color) +
326            printedTypeParameters(LEN) +
327            printedModifiers(LEN) +
328            printedExtendsImplements(LEN) +
329            printedMethodField(LEN, color) + 
330            printedLocation(LEN, color, (flags & BRIEF_LOCATION) > 0) +
331
332            // The previous method does not add a '\n' end to the end of the returned string
333            // This is optional, it adds a '\n' AT THE BEGINNING if it is included
334
335            printedComments(LEN, color, (flags & JAVADOC_COMMENTS) > 0);
336    }
337
338    private String printedMethodField(int numSpaces, boolean color)
339    {
340        return
341            StringParse.rightSpacePad("Num Methods:", numSpaces) +
342                '[' + (color ? BGREEN : "") + numMethods + (color ? RESET : "") + "]\n" +
343            StringParse.rightSpacePad("Num Fields:", numSpaces) +
344                '[' + (color ? BGREEN : "") + numFields + (color ? RESET : "") + "]\n";
345    }
346
347    private String printedExtendsImplements(int LEN)
348    {
349        return
350            (((this.extendedTypesJOW != null) && (this.extendedTypesJOW.size() > 0))
351                ? (StringParse.rightSpacePad("Extends:", LEN) + '[' +
352                    StrCSV.toCSV(extendedTypesJOW, true, false, null) + "]\n")
353                : "") +
354            (((this.implementedTypesJOW != null) && (this.implementedTypesJOW.size() > 0))
355                ? (StringParse.rightSpacePad("Implements:", LEN) + '[' +
356                    StrCSV.toCSV(implementedTypesJOW, true, false, null) + "]\n")
357                : "");
358    }
359
360    private String printedTypeParameters(int numSpaces)
361    {
362        if ((this.genericTypeParameters == null) || (this.genericTypeParameters.size() == 0))
363            return "";
364
365        return
366            StringParse.rightSpacePad("Type-Parameters:", numSpaces) +
367            '[' + StrCSV.toCSV(genericTypeParameters, true, true, MAX_STR_LEN) + "]\n";
368    }
369
370
371    // *************************************************************************************
372    // *************************************************************************************
373    // Clone, CompareTo & Equals Stuff
374    // *************************************************************************************
375    // *************************************************************************************
376
377
378    /**
379     * Java's {@code interface Comparable<T>} requirements.  This does a very simple comparison
380     * using the two nested-types's {@code 'name'} field.
381     * 
382     * @param nt Any other {@code Nested-Type} to be compared to {@code 'this'}
383     * 
384     * @return An integer that fulfills Java's
385     * {@code interface Comparable<NestedType>, public boolean compareTo(NestedType nt)} method
386     * requirements.
387     */
388    public int compareTo(NestedType nt)
389    { return (this == nt) ? 0 : this.name.compareTo(nt.name); }
390
391    /**
392     * This <I>should be called an "atypical version" of </I> the usual {@code equals(Object
393     * other)} method.  This version of equals merely compares the name of the field defined.  The
394     * presumption here is that the definition of a 'field' only has meaning - <I>at all</I> -
395     * inside the context of a {@code class, interface, } or {@code enumerated-type} where that
396     * field is defined.  Since inside any {@code '.java'} source-code file, there may only be one
397     * field with a given name, this method shall return {@code TRUE} whenever the field being
398     * compared also has the same name.
399     * 
400     * @param other This may be any other field.  It is <I><B>strongly suggested</B></I> that
401     * {@code 'other'} be a field defined in the same {@code '.java'} source-code file as
402     * {@code 'this'} field.
403     * 
404     * @return This method returns {@code TRUE} when {@code 'this'} instance of {@code Field} has
405     * the same {@code 'name'} as the name-field of input-parameter {@code 'other'}
406     */
407    public boolean equals(NestedType other)
408    { return this.name.equals(other.name); }
409}