Futilities/v0.6/clade/Kiosk/WUIDL

From Woozle Writes Code
< Futilities‎ | v0.6‎ | clade‎ | Kiosk
Jump to navigation Jump to search
clade: Kiosk\WUIDL
Clade Family
Kiosk WUIDL (none)
Clade Aliases
Alias Clade
AppClass [ca] App
Array* [c,i] Sys\Data\Things\Array\DStor
Base* [ca,i] Kiosk
ConfigAdmin [cs] boot\load\Configs
ErrorClass Sys\Events\Message\Error
FListIface Sys\Data\tree\XML\tags\base\FoundList
MarksClass Sys\Data\Codec\aux\Marks\default\Var
QObj* [c,i] Data\Mem\QVar\Obj
QStr* [c,i] Data\Mem\QVar\Str
TInstDocRootIface Sys\Data\tree\XML\tags\wuidl\Found
TInstItemIface Data\tree\XML\tags\item\Found
Subpages

About

  • source: code doc
  • Purpose: Woozalien User Interface Definition Language
    • Reads all basic options-UI definitions from an XML file
  • Sequence:
    • Setup() - called the first time QCodec() is called, I think?
      • ParseSpecFile() - read the spec definition XML and abstract it into tag objects

Thinking

  • 2024-11-04 This is where we differentiate between the various Futils applications, in the definition of what Action each Option corresponds to. This is the abstract top-level Opts class for WUIDL-configured apps (which will be all of the Futils).

History

  • 2024-10-08 Adding Matcher(), Lookup()
    • for now, MarksClass is not configurable, but that could easily be remedied.
  • 2024-10-24
    • removed ProcessData() -- nothing calls it
    • added SetupOptions() -- now called just before ParseInput()
  • 2024-11-11 Commented out code in ParseOptions(), with the note: "What if we... just don't do any indexing? Can the tags now find info instead?"
  • 2024-12-02 This can't be working properly now, because it has "Aux" instead of "aux" in the namespace...
  • 2026-02-13 The above issue must have been fixed at some point. It's at least partly working now, and has been fully working at times since then.

Methods

Code

interface iWUIDL extends BaseIface {
    // INFO
    function DocsURL() : string;
    function UpdateString() : string;
    function VersionString() : string;
    // INFO: lookup
    function FetchValue(string $sKey) : QStrIface;
}
abstract class caWUIDL extends BaseClass implements iWUIDL {

    // ++ CONFIG ++ //

    protected const FN_UI_DEF = 'ui'; // name of UI definition XML file (extension is .xml)

    // -- CONFIG -- //
    // ++ SETUP ++ //

    /**
     * PUBLIC so App can call it at startup time
     * HISTORY:
     *  2024-11-13 renamed SetupOptions() -> Setup(), and made it PUBLIC
     *  2025-11-17 removed call to ParseInput()
     */
    public function Setup() {
        $this->ParseSpecFile();
    }

    // ++ SETUP: internals ++ //

    // ACTION: read the spec definition XML and abstract it into tag objects
    protected function ParseSpecFile() {
        $oScrn = self::Screen();
        $ok = FALSE;
        $osData = ConfigAdmin::QData();
        if ($osData->HasIt()) {
            $arConf = $osData->GetIt();
            if (array_key_exists(self::FN_UI_DEF,$arConf)) {
                $oCodec = $arConf[self::FN_UI_DEF];
                $this->QCodec()->SetIt($oCodec);
                $ok = TRUE;
            } else {
                #echo "UI definition isn't right somehow. Data:\n".print_r($arConf,TRUE);
                echo "UI definition isn't right somehow.".CRLF;
                $oaConf = ArrayClass::FromArray($arConf);
                echo $oaConf->RenderAsTable();
            }
        }
        if ($ok) {
            #$ftArr = print_r($arUI,TRUE);
            #echo $oCodec->QTag()->GetIt()->ReflectObject()->Report();
            #$ftConf = $oCodec->QTag()->GetIt()->DumpFull();
            #$sName = $this->Name();
            #$ftName = $oScrn->XMLTagIt($sName);
            #echo "CONF DATA LOADED:\n$ftConf\n";
            #echo "INDEX:\n".print_r($arDX,TRUE)."\n";
        } else {
            echo "Could not load UI definition.\n";
        }
    }

    // -- SETUP -- //
    // ++ INPUT ++ //

    /**
     * ACTION: match the input arguments up against the available (app) parameters, and turn them into an Action sequence
     */
    public function ReadInput() {
        // + OBJECTS
        $oScrn = self::Screen();
        $osDex = $this->QInDadex();  // object encapsulating the input command
        // - OBJECTS

        // Iterate through the input command/request and add InReqs to the Actions() list:

        if ($osDex->HasIt()) {
            #self::GotToHere();
            $oDex = $osDex->GetIt();
            $oaActs = $this->Actions();
            while ($oDex->HasData()) {
                $oPair = $oDex->FirstPairPull();  // get the next argument
                $sArgKey = $oPair->Name();  // key for input argument
                #$this->AmHere("ArgKey=[$sArgKey]");

                $oTInst = $this->MainTag();
                $oCues = $oTInst->GetFTags()->InputCues();
                // search Cues for input commandlet ($sArgKey)
                $osTDCue = $oCues->QryIt($sArgKey);
                if ($osTDCue->HasIt()) {
                    $oTDCue = $osTDCue->GetIt();  // [WF]Sys\Data\tree\XML\tags\cue\cFound
                    $oTDItem = $oTDCue->GetOuter();

                    #$this->AmHere('$oTDItem class: '.get_class($oTDItem));

                    // create an Action for this option-tag
                    $scAct = $this->ItemInReqClass($oTDItem);             // look up Action class for the reqItem holding this cue
                    $sArgVal = $oPair->HasValue() ? $oPair->Value() : ''; // input argument value, if any

                    //+DEBUG
                    #$oTTItem = $oTDItem->Type();
                    #echo $oTTItem->ReflectThis()->Report(); // DEBUG
                    #$sTag = $oTTItem->Name(); // name of tag-type
                    #$this->AmHere(" -- ArgName=[".$oPair->Name()."] tag=[$sTag] ArgVal=[$sArgVal]");
                    //-DEBUG

                    $oAct = ($scAct)::FromItemPiece($oTDItem,$oPair);  // create Action-object for it
                    $oaActs->Append($oAct);                    // add Action to the sequence
                } else {
                    $oErr = new ErrorClass("input argument [$sArgKey] not recognized.");
                    $this->Errors()->AddMsgObject($oErr);
                }
            }
        }
    }

    // ++ INPUT: app terms ++ //

    // CEMENT: Futils base Kiosk
    protected function AppInTerms() : FListIFace {
        $oVals = $this->MainTag()->GetFTags()->FTagFor_ReqVals();
        return $oVals->GetFTags();
    }

    // -- INPUT -- //
    // ++ OUTPUT ++ //

    public function RenderAppUsage() : string { return AppClass::SelfName().' '.$this->MainTag()->TagFor_Request()->FormatString(); }

    // DEFAULT
    private $didShow = FALSE;
    protected function RenderHelpText() : string {
        #if ($this->didShow) return ''; // 2024-12-27 This is a bit of a kluge, to prevent this being shown multiple times. TODO: Refine later.
        $this->didShow = TRUE;

        $oScrn = self::Screen();

        #echo $this->MainTag()->ReflectThis()->Report();

        $oTerms = $this->AppInTerms();
        $oaTerms = $oTerms->OAFound();
        #echo $oTerms->ReflectThis()->Report();
        $nTerms = $oaTerms->Count();

        self::HardAssert($nTerms > 0,'No command options defined in ui.xml.'); // This should never happen here.

        // construct the table of options

        $oTable = $oScrn->NewTable();

        // ->GetFirst() is a bit of a kluge, here; TODO: refine

        $oType = $oaTerms->GetFirst()->Type();
        $oRow = $oTable->RowFromData($oType->HelpHeaderAsArray());
        $oRow->IsHeader(TRUE);
        foreach ($oaTerms as $sKey => $oSub) {
            $oTable->RowFromData($oSub->HelpLineAsArray());
        }
        $sOut = 'Available command options:'.CRLF.$oTable->Render();

        return $sOut;
    }

    // -- OUTPUT -- //
    // ++ ACTION ++ //

    public function RunInput() {
        $oaActs = $this->Actions();
        $arActs = $oaActs->GetVals();
        foreach ($arActs as $oAct) {
            $oHow = $oAct->HowItWent();
            if ($oHow->AnyMessages()) {
                echo $oHow->RenderMessages();
            }
            if ($oHow->WasTried()) {
                if ($oHow->GetOkay()) {
                    echo 'Action already completed...?'.CRLF; // TODO: name the action(s)
                } else {
                    echo 'Stopping because of an input error.'.CRLF;    // TODO: name the action
                }
            } else {
                $oAct->Go();
            }
        }
    }

    // -- ACTION -- //
    // ++ OBJECTS ++ //

    private $osCodec = NULL;
    protected function QCodec() : QObjIface { return $this->osCodec ?? ($this->osCodec = $this->NewQCodec()); }
    protected function NewQCodec() : QObjIface { return new QObjClass; }

    // -- OBJECTS -- //
    // ++ INFO ++ //

    // SHORTCUT
    #public function CmdFmt() : string { return $this->FTagFor_AppItem()->CmdFmt(); }
    // SHORTCUT
    public function DocsURL() : string { return $this->FTagFor_AppItem()->DocsURL(); }
    // SHORTCUT
    public function UpdateString() : string { return $this->FTagFor_AppItem()->UpdateString(); }
    // SHORTCUT
    public function VersionString() : string { return $this->FTagFor_AppItem()->VersionString(); }

    // ++ INFO: lookup ++ //

    abstract protected function InReqClassFor(string $sName) : ?string;

    protected function ItemInReqClass(TInstItemIface $o) : string {
        $oaAttrs = $o->Attrs();
        #$oPair = $oaAttrs->QName()->GetIt();
        #$sItName = $oPair->Value();   // item name (not the attribute name; the name-attribute's value)
        #echo $o->ReflectThis()->Report();
        $sItName = $o->SName();

        $scAct = $this->InReqClassFor($sItName);
        if (is_null($scAct)) {
            $ftse = self::Screen()->BlueIt(eKiosk::class);
            echo self::CodingPrompt("Need to have a case in $ftse for the '$sName' item."); die();
        }
        return $scAct;
    }

    public function FetchValue(string $sExpr) : QStrIface {
        self::GotToHere("EXPRESSION=[$sExpr]");
        $os = QStrClass::AsNew();
        #echo $this->ReflectObject()->Report(); die();
        // WORKING HERE
        // TODO 2024-10-13 parse *pieces* of each value (for GetSubOpts() (now GetSubTerms()), I guess?); for now, we'll just treat them as simple varnames.
        $oTagRoot = $this->MainTag();
        #echo $oTagRoot->ReflectObject()->Report(); die();
        $osTag = $oTagRoot->QATag($sExpr);
        if ($osTag->HasIt()) {
            echo "ACCESSING tag [$sExpr]\n";
            $oTagFnd = $osTag->GetIt();
            #echo $oTagFnd->ReflectObject()->Report();
            // TODO: This is where we would pass in the rest of the expression, if we had that parsed out.
            $os->SetIt($oTagFnd->RenderCmdFmt());
        } else {
            $this->AddError("Encountered [$sExpr] in <cmdfmt>, but can't find a top-level tag for it.");   // TODO: list available tags
        }
        return $os;
    }

    // -- INFO -- //
    // ++ OBJECTS ++ //

    private $oVars = NULL; protected function Lookup() : LookupIface { return $this->oVars ?? ($this->oVars = $this->NewLookup()); }
    protected function NewLookup() : LookupIface { return ($this->LookupClass())::FromKiosk($this); }
    private $oFind = NULL; protected function Matcher() : MatcherIface { return $this->oFind ?? ($this->oFind = $this->NewMatcher()); }
    protected function NewMatcher() : MatcherIface { return new ($this->MatcherClass())(new MarksClass, $this->Lookup()); }

    private $oaActs = NULL;
    protected function Actions() : ArrayIface { return $this->oaActs ?? ($this->oaActs = ArrayClass::AsNew()); }

    // RETURNS: object for the WUIDL tag
    protected function MainTag() : TInstDocRootIface {
        $osCodec = $this->QCodec();
        $this->HardAssert($osCodec->HasIt(),'QCodec() has not been set.');  // TODO: Maybe should be a coding-prompt.
        $oCodec = $osCodec->GetIt();
        #echo 'CODEC CLASS: '.get_class($oCodec).CRLF;
        $oTop = $oCodec->QTag()->GetIt(); // This should be the WUIDL tag.
        return $oTop;
    }
    // RETURNS: object for the <item> subtag in <wuidl>

    protected function FTagFor_AppItem() : TInstItemIface {
        $ofMain = $this->MainTag();
        $ofList = $ofMain->GetFTags();
        return $ofMain->AppItemFTag();
    }

}