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