Futilities/v0.6/clade/Kiosk/WUIDL

From Woozle Writes Code
< Futilities‎ | v0.6‎ | clade‎ | Kiosk
Revision as of 14:44, 17 November 2025 by Woozle (talk | contribs) (oopsy)
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...

Methods

public

// INFO

// INFO: lookup

Code

as of 2025-11-17

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();
    }

}