001package Torello.JavaDoc;
002
003
004// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
005// Standard-Java Imports
006// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
007
008import java.io.IOException;
009import java.util.Optional;
010
011
012// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
013// Java-HTML Imports
014// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
015
016import Torello.Java.*;
017
018import static Torello.Java.C.*;
019import static Torello.JavaDoc.PF.*;
020
021import Torello.Java.Additional.Ret2;
022import Torello.Java.ReadOnly.ReadOnlyList;
023import Torello.Java.ReadOnly.ROArrayListBuilder;
024
025
026// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
027// JDUInternal
028// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
029
030import Torello.JDUInternal.Annotations.EntityAnnotations.EntityAnnotationData;
031import Torello.JDUInternal.Parse.HTML.Signature.D1_CallableSignature;
032
033
034// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
035// The new Source-Code Parser: com.sun.source.*
036// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
037
038import com.sun.source.tree.MethodTree;
039
040
041/**
042 *<B CLASS=JDDescLabel>Reflection Class:</B>
043 * 
044 * <BR />Holds all information extracted from <CODE>'&#46;java'</CODE> Source-Files about Method's
045 * identified in that file.
046 * 
047 * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_GET_INST>
048 * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_METHOD>
049 */
050@JDHeaderBackgroundImg(EmbedTagFileID={"REFLECTION_EXTENSION"})
051public class Method
052    extends Callable
053    implements java.io.Serializable, Comparable<Method>, Cloneable
054{
055    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
056    public static final long serialVersionUID = 1;
057
058    /**
059     * The return type of the {@code method}, as a {@code String}.  If this is a method with a
060     * {@code 'void'} return-type, this shall be "void".
061     */
062    public final String returnType;
063
064    /**
065     * The return type of the {@code method}, as a {@code String}.
066     * <BR /><BR />
067     * <EMBED CLASS='external-html' DATA-FILE-ID=JPB_JOW_TITLE>
068     */
069    public final String returnTypeJOW;
070
071
072    // ********************************************************************************************
073    // ********************************************************************************************
074    // Reference-Hook: com.sun.source.tree
075    // ********************************************************************************************
076    // ********************************************************************************************
077
078
079    /**
080     * <EMBED CLASS='external-html' DATA-FILE-ID=SSTB_HOOK_FIELD>
081     * 
082     * If a user decides to make use of the native Oracle {@code MethodTree} instance that was
083     * used to build this {@code Method} instance, it may be retrieved from this {@code transient}
084     * field.
085     */
086    public final transient MethodTree methodTree;
087
088
089    // ********************************************************************************************
090    // ********************************************************************************************
091    // Constructor - com.sun.source.tree 
092    // ********************************************************************************************
093    // ********************************************************************************************
094
095    
096    // Public, but internally-used-only Constructor for "Method"
097    public Method(
098            final MethodTree            mt,
099            final EntityAnnotationData  ead,
100            final TreeUtils             util
101        )
102    {
103        super(mt, mt.getName().toString(), Entity.METHOD, ead, util);
104
105        this.returnType     = mt.getReturnType().toString();
106        this.returnTypeJOW  = StrSource.typeToJavaIdentifier(this.returnType);
107        this.methodTree     = mt;
108    }
109
110
111    // ********************************************************************************************
112    // ********************************************************************************************
113    // Constructor: Used Internally by SignatureParse / SummaryHTMLFile
114    // ********************************************************************************************
115    // ********************************************************************************************
116
117
118    // Ensures that the Version with longer type-information strings is used.
119    // Java Doc often uses longer type strings than is available from the source-code parse
120    // Remember, JavaParser Symbol-Solver doesn't work well, and the Sun/Oracle Parser doesn't have
121    // a linker at all.
122    //
123    // Called from JDUInternal.ParseHTML.SignatureParse:
124    //      This is used when the JavaDocHTMLFile is asking that a method be retrieved based on
125    //      input from **BOTH** the HTML-File **AND** the Source-File
126
127    public Method(final D1_CallableSignature cSig, final Method mFromSourceParser)
128    {
129        // Does the same thing as the loop statement below, but for the "parameterTypes"
130        super(cSig, mFromSourceParser);
131
132        // Java Doc always produces "java.lang.String", while JP just gives "String"
133        //
134        // REMEMBER: JP is lazy when it comes to "Package Information" for types.
135        //           Java-Doc includes it often - BUT NOT ALWAYS.  (See above comment)
136        //
137        // Remember, though, the rest of the JavaParser fields are filled out, Java Doc
138        // leaves out all the other information that JP retrieves.
139
140        this.returnType =
141            (cSig.returnType.length() > mFromSourceParser.returnType.length())
142                ? cSig.returnType
143                : mFromSourceParser.returnType;
144
145        // This should never matter.  They must be identical.
146        this.returnTypeJOW = mFromSourceParser.returnTypeJOW;
147
148        // Save the reference hook
149        this.methodTree = mFromSourceParser.methodTree;
150    }
151
152    // Used for "Synthetic Methods ONLY!"  Literally, this line is the only way this particular
153    // Constructor could ever be called:
154    //
155    // From JDUInternal.ParseHTML.SignatureParse:
156    //      if (StrCmpr.equalsXOR(cSig.name, "valueOf", "values")) return new Method(cSig);
157
158    public Method(final D1_CallableSignature cSig)
159    {
160        // Does the same thing as the loop statement below, but for the "parameterTypes"
161        super(cSig, Entity.METHOD);
162
163        this.returnType         = cSig.returnType;
164        this.returnTypeJOW      = cSig.returnTypeJOW;
165        this.methodTree         = null;
166    }
167
168
169    // ********************************************************************************************
170    // ********************************************************************************************
171    // toString()
172    // ********************************************************************************************
173    // ********************************************************************************************
174
175
176    /**
177     * Generates a string of this method, with most information included.
178     * 
179     * <BR /><BR /><B CLASS=JDDescLabel>Reduced Information:</B>
180     * 
181     * <BR />This {@code 'toString'} will not return every piece of information contained by this
182     * class. For example, both the method body, and any possible JavaDoc Comments are not
183     * included.  For a more enhanced {@code toString()} method version, invoke the 
184     * {@link #toString(int) toString(PrintFlags)} method which accepts {@link PF Print-Flags}.
185     * 
186     * @return A printable {@code String} of this method.
187     * @see PF
188     * @see #toString(int)
189     */
190    public String toString()
191    {
192        return
193            "Name:            [" + name + "]\n" +
194            "Signature:       [" + StrPrint.abbrevEndRDSF(signature, MAX_STR_LEN, true) + "]\n" +
195            "Modifiers:       [" + StrCSV.toCSV(modifiers, true, true, null) + "]\n" +
196            printedParameterNamesTS() +
197            printedParameterTypesTS() +
198            "Return Type:     [" + returnType + "]\n" +
199            printedExceptionsTS() +
200
201            // This will **NEVER** be null - unless 'this' instance was built from an HTML File,
202            // rather than a source-code file.  Instances like that are only used temporarily, and
203            // are garbage collected instantly.  Do this check anyway (just in case).
204
205            "Location:        " + ((this.location == null)
206                ? "null" 
207                : ('[' + this.location.quickSummary() + ']'));
208    }
209
210    /**
211     * <EMBED CLASS='external-html' DATA-FILE-ID=TO_STR_PF>
212     * @param flags These are defined in the {@link PF Print-Flags} class
213     * @return A printable {@code String} of this {@code Method}.
214     * @see #toString()
215     * @see PF
216     */
217    public String toString(int flags)
218    {
219        boolean color = (flags & UNIX_COLORS) > 0;
220        Ret2<Boolean, Boolean> jow = jowFlags(flags);
221
222        return 
223            printedName("Method", 20, color) +
224            printedSignature(20, color) +
225            printedModifiers(20) +
226            printedParamNames() + 
227            printedParamTypes(jow) +
228            printedReturnType(jow, color) +
229            printedExceptions() +
230            printedLocation(20, color, (flags & BRIEF_LOCATION) > 0) +
231
232            // The previous method does not add a '\n' end to the end of the returned string
233            // These are both optional, they add a '\n' AT THE BEGINNING if one of them is included
234
235            printedComments(20, color, (flags & JAVADOC_COMMENTS) > 0) +
236            printedCallableBody(flags);
237    }
238
239    private String printedReturnType(Ret2<Boolean, Boolean> jow, boolean color)
240    {
241        String rt = null, rtJOW = null;
242
243        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
244        // Worry about the colors first
245        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
246
247        if (jow.a || jow.b)
248            rtJOW = color ? (BGREEN + returnTypeJOW + RESET) : returnTypeJOW;
249
250        if (! jow.b)
251            rt = color ? (BGREEN + returnType + RESET) : returnType;
252        
253
254        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
255        // Now print the string
256        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
257
258        if (jow.b /*onlyJOW*/) return "Return Simple-Type: [" + rtJOW + "]\n";
259
260        else if (jow.a /*addJOW*/) return
261            "Return Type:        [" + rt + "]\n" +
262            "Return Simple-Type: [" + rtJOW + "]\n";
263
264        else return "Return Type:        [" + rt + "]\n";
265    }
266
267
268    // ********************************************************************************************
269    // ********************************************************************************************
270    // CompareTo & Equals
271    // ********************************************************************************************
272    // ********************************************************************************************
273
274
275    /**
276     * Java's {@code interface Comparable<T>} requirements.  This does a very simple comparison
277     * using the two method's {@link #name} field. If the name comparison will not
278     * suffice in making a decision, then the number of parameters, and parameter-types are used
279     * to making the sort-decision.
280     * 
281     * @param m Any other {@code Method} to be compared to {@code 'this' Method}
282     * 
283     * @return An integer that fulfills Java's {@code Comparable<Method>} interface
284     * requirements.
285     */
286    public int compareTo(Method m)
287    {
288        // Identical References
289        if (this == m) return 0;
290
291        // Sorting by ignoring case is best - usually for the looks of a list
292        // NOTE: Returning '0' is bad, because a TreeSet will remove duplicate-elements.  This
293        //       means two different meethods with the same name would not "fit" into a treeset.
294
295        int ret = this.name.compareToIgnoreCase(m.name);
296        if (ret != 0) return ret;
297
298        // Try to identify a different without ignoring case.
299        ret = this.name.compareTo(m.name);
300        if (ret != 0) return ret;
301
302        ret = this.numParameters() - m.numParameters();
303        if (ret != 0) return ret;
304
305        for (int i=0; i < this.parameterTypesJOW.size(); i++)
306        {
307            ret = this.parameterTypesJOW.get(i).compareTo(m.parameterTypesJOW.get(i));
308            if (ret != 0) return ret;
309        }
310
311        return 0;
312    }
313
314    /**
315     * This <I>should be called an "atypical version" of </I>the usual {@code equals(Object other)}
316     * method.  This version of equals merely compares the name and parameters-list of the method.
317     * The presumption here is that the definition of a 'method' only has 
318     * meaning - <I>at all</I> - inside the context of a {@code class, interface, } or
319     * {@code enumerated-type} where that method is defined. Since inside any {@code '.java'}
320     * source-code file, there may only be one method with a given name and parameter-list, this
321     * shall return {@code TRUE} whenever the method being compared has the same name and parameter
322     * types as {@code 'this'} does.
323     * 
324     * @param other This may be any other {@code method}.  It is <I><B>strongly suggested</B></I>
325     * that this be a {@code method} defined in the same {@code '.java'} source-code file as
326     * {@code 'this' method}.
327     * 
328     * @return This method returns {@code TRUE} when {@code 'this'} instance of {@code Method} has
329     * <B>both</B> the same {@code public final Sting name} <B>and</B> the same parameter-list as
330     * {@code 'other'}.
331     */
332    public boolean equals(Method other)
333    {
334        // The method's must have the same name!
335        if (! this.name.equals(other.name)) return false;
336
337        // If the number of parameters in the 'other' instance of Method differ from the number
338        // of parameters in 'this' Method, then return FALSE immediately.  It cannot be a match.
339
340        if (this.numParameters() != other.numParameters()) return false;
341
342        // If both of these have zero parameters, and their names have matched, return true
343        // immediately
344
345        if (this.numParameters() == 0) return true;
346
347        // If any of the parameter-names are different, break immediately and return false;
348        for (int i=0; i < this.parameterNames.size(); i++)
349            if (! this.parameterNames.get(i).equals(other.parameterNames.get(i))) return false;
350
351        // If the parameter-types listed by the javadoc '.html' file differ from the parameter
352        // types listed in the original '.java' source-code file, then break immediately.
353        //
354        // NOTE: The "package-information" for the FULL CLASS OR INTERFACE NAME is not always
355        //       available.
356
357        for (int i=0; i < this.parameterTypesJOW.size(); i++)
358            if (! this.parameterTypesJOW.get(i).equals(other.parameterTypesJOW.get(i)))
359                return false;
360
361        // ALL TEST PASSED
362        return true;
363    }
364
365
366    // ********************************************************************************************
367    // ********************************************************************************************
368    // A.I. Token-Stream Generator-Method
369    // ********************************************************************************************
370    // ********************************************************************************************
371
372
373    public ReadOnlyList<ReadOnlyList<String>> getTokenStreams()
374    {
375        final ROArrayListBuilder<ReadOnlyList<String>>  tokens  = new ROArrayListBuilder<>();
376        ROArrayListBuilder<String>                      b       = new ROArrayListBuilder<>();
377
378        tokens.add(ReadOnlyList.of("SECTION", "entity", "method"));
379        tokens.add(ReadOnlyList.of("SECTION", "name", this.name));
380        tokens.add(ReadOnlyList.of("SECTION", "signature", this.signature));
381        tokens.add(ReadOnlyList.of("SECTION", "return-type", this.returnTypeJOW));
382
383        if ((this.parameterNames != null) && (this.parameterNames.size() > 0))
384        {
385            b.add("SECTION");
386            b.add("parameters");
387            for (int i = 0; i < this.parameterNames.size(); i++)
388                b.add(this.parameterTypesJOW.get(i) + " " + this.parameterNames.get(i));
389            tokens.add(b.build());
390        }
391
392        if ((this.modifiers != null) && (modifiers.size() > 0))
393        {
394            b = new ROArrayListBuilder<>();
395            b.add("SECTION");
396            b.add("modifiers");
397            for (String m : this.modifiers) b.add(m);
398            tokens.add(b.build());
399        }
400
401        if ((this.exceptions != null) && (this.exceptions.size() > 0))
402        {
403            b = new ROArrayListBuilder<>();
404            b.add("SECTION");
405            b.add("throws");
406            for (String exn : this.exceptions) b.add(exn);
407            tokens.add(b.build());
408        }
409
410        if ((this.annotations != null) && (annotations.size() > 0))
411        {
412            b = new ROArrayListBuilder<>();
413            b.add("SECTION");
414            b.add("annotations");
415            for (String a : this.annotations) b.add(a);
416            tokens.add(b.build());
417        }
418
419        return tokens.build();
420    }
421
422}