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