// @receiver-class: JadeScript findPrinterPrintUnformattedCallers(); // -------------------------------------------------------------------------------- // Purpose: List every compiled reference to printUnformatted on the RootSchema // Printer class, and to every reimplementation of it anywhere in the // Printer hierarchy. The report goes to the write window AND to a // date-and-time-stamped log file. // // Parameters: none // // Notes: THE LOG DIRECTORY IS NOT HARDWIRED. JadeLog.filePath is left null and // fileName carries no path, which per the filePath property contract // sends the file to the directory named by the LogDirectory parameter in // the [JadeLog] section of the Jade initialisation file - the same // directory jommsg.log is written to. // // formatOutput is forced false. Left at its default of true, JadeLog // prefixes every line with date, time and process-thread ids, and the // first line could then never read EXACTLY as the banner requires. // // The report accumulates in a local String with CrLf line delimiters, // NOT in a StringArray: on older releases the membership of a string // array is capped at a length these report lines can exceed. A local // String carries no such cap. // // The report is buffered rather than written as it is produced: whether // the banner is needed is only known after the whole hierarchy has been // walked, and the banner has to come before everything else in BOTH // sinks. // // The implementation set is closed to a FIXPOINT rather than taken from // a single sweep: Class::getImplementors seeds it across the schema // hierarchy, then Method::getImplementors expands every method found // until nothing new appears. Neither call's search depth is assumed, so // a reimplementation of a reimplementation cannot be missed. // // FeatureUsage.feature is the inverse partner of BOTH Feature.scriptRefs // and Feature._sysScriptRefs, so a reference lands in exactly one of the // two collections; both are gathered or the answer under-reports. // -------------------------------------------------------------------------------- vars printerClass : Class; target : Method; resolved : Boolean; impls : MethodSet; pending : MethodSet; found : MethodSet; pendingMeth : Method; sub : Method; impl : Method; implType : Type; label : String; usages : FeatureUsageSet; usage : FeatureUsage; callers : ScriptSet; caller : Script; ownerType : Type; totalCallers : Integer; report : String; logFile : JadeLog; now : TimeStamp; stamp : String; begin create impls transient; create pending transient; create found transient; create usages transient; create callers transient; create logFile transient; printerClass := rootSchema.getClass("Printer"); if printerClass = null then report := report & "RootSchema class Printer not found" & CrLf; else target := printerClass.getMethod("printUnformatted"); if target = null then report := report & "Printer::printUnformatted not found" & CrLf; else resolved := true; // Seed with the RootSchema method itself plus the hierarchy-wide sweep. impls.add(target); printerClass.getImplementors(target.name, found); foreach sub in found do if not impls.includes(sub) then impls.add(sub); endif; endforeach; // Close to a fixpoint: expand every implementation found, to any depth. impls.copy(pending); while not pending.isEmpty() do pendingMeth := pending.first(); pending.remove(pendingMeth); found.clear(); pendingMeth.getImplementors(found); foreach sub in found do if not impls.includes(sub) then impls.add(sub); pending.add(sub); endif; endforeach; endwhile; report := report & "printUnformatted implementations in the Printer hierarchy: " & impls.size().String & CrLf; if impls.size() = 1 then report := report & "No reimplementations: RootSchema Printer::printUnformatted is the only implementation." & CrLf; endif; foreach impl in impls do usages.clear(); callers.clear(); if impl.scriptRefs <> null and impl.scriptRefs.size > 0 then impl.scriptRefs.copy(usages); endif; if impl._sysScriptRefs <> null and impl._sysScriptRefs.size > 0 then impl._sysScriptRefs.copy(usages); endif; foreach usage in usages do caller := usage.getSchemaScript(); if caller <> null and not callers.includes(caller) then callers.add(caller); endif; endforeach; totalCallers := totalCallers + callers.size(); if impl = target then label := "Callers of "; else label := "Callers of reimplementation "; endif; implType := impl.getSchemaType(); report := report & CrLf; if implType = null then report := report & label & impl.getSchema().name & "::" & impl.name & CrLf; else report := report & label & impl.getSchema().name & "::" & implType.name & "::" & impl.name & CrLf; endif; report := report & " call sites : " & usages.size().String & CrLf; report := report & " distinct callers : " & callers.size().String & CrLf; foreach caller in callers do ownerType := caller.getSchemaType(); if ownerType = null then report := report & " " & caller.getSchema().name & "::" & caller.name & CrLf; else report := report & " " & caller.getSchema().name & "::" & ownerType.name & "::" & caller.name & CrLf; endif; endforeach; endforeach; endif; endif; // Banner first, in both sinks, when nothing anywhere calls printUnformatted. if resolved and totalCallers = 0 then report := "NO CALLERS of Printer::printUnformatted" & CrLf & report; endif; // Date-and-time-stamped name, no path: filePath stays null, so Jade resolves the // directory from [JadeLog] LogDirectory itself. now := app.actualTime(); stamp := now.date().year().String.padLeadingZeros(4) & now.date().month().String.padLeadingZeros(2) & now.date().day().String.padLeadingZeros(2) & "_" & now.time().hour().String.padLeadingZeros(2) & now.time().minute().String.padLeadingZeros(2) & now.time().second().String.padLeadingZeros(2); logFile.fileName := "printUnformattedCallers_" & stamp; logFile.formatOutput := false; logFile.versionFile := false; write report; logFile.info(report); logFile.commitLog(); // Window-only trailer, so it cannot disturb the first line of either sink. write ""; write "Output also written to " & logFile.getActualFileName(); epilog delete impls; delete pending; delete found; delete usages; delete callers; delete logFile; end;