MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Linq;6using System.Collections.ObjectModel;7using System.Management.Automation;8using System.Management.Automation.Internal;9 10namespace Microsoft.PowerShell.Commands.Internal.Format11{12 /// <summary>13 /// Base class for the various types of formatting shapes.14 /// </summary>15 internal abstract class ViewGenerator16 {17 internal virtual void Initialize(TerminatingErrorContext terminatingErrorContext,18 PSPropertyExpressionFactory mshExpressionFactory,19 TypeInfoDataBase db,20 ViewDefinition view,21 FormattingCommandLineParameters formatParameters)22 {23 Diagnostics.Assert(mshExpressionFactory != null, "mshExpressionFactory cannot be null");24 Diagnostics.Assert(db != null, "db cannot be null");25 Diagnostics.Assert(view != null, "view cannot be null");26 27 errorContext = terminatingErrorContext;28 expressionFactory = mshExpressionFactory;29 parameters = formatParameters;30 31 dataBaseInfo.db = db;32 dataBaseInfo.view = view;33 dataBaseInfo.applicableTypes = DisplayDataQuery.GetAllApplicableTypes(db, view.appliesTo);34 35 InitializeHelper();36 }37 38 internal virtual void Initialize(TerminatingErrorContext terminatingErrorContext,39 PSPropertyExpressionFactory mshExpressionFactory,40 PSObject so,41 TypeInfoDataBase db,42 FormattingCommandLineParameters formatParameters)43 {44 errorContext = terminatingErrorContext;45 expressionFactory = mshExpressionFactory;46 parameters = formatParameters;47 dataBaseInfo.db = db;48 49 InitializeHelper();50 }51 52 /// <summary>53 /// Let the view prepare itself for RemoteObjects. Specific view generators can54 /// use this call to customize display for remote objects like showing/hiding55 /// computername property etc.56 /// </summary>57 /// <param name="so"></param>58 internal virtual void PrepareForRemoteObjects(PSObject so)59 {60 }61 62 private void InitializeHelper()63 {64 InitializeFormatErrorManager();65 InitializeGroupBy();66 InitializeAutoSize();67 InitializeRepeatHeader();68 }69 70 private void InitializeFormatErrorManager()71 {72 FormatErrorPolicy formatErrorPolicy = new FormatErrorPolicy();73 if (parameters != null && parameters.showErrorsAsMessages.HasValue)74 {75 formatErrorPolicy.ShowErrorsAsMessages = parameters.showErrorsAsMessages.Value;76 }77 else78 {79 formatErrorPolicy.ShowErrorsAsMessages = this.dataBaseInfo.db.defaultSettingsSection.formatErrorPolicy.ShowErrorsAsMessages;80 }81 82 if (parameters != null && parameters.showErrorsInFormattedOutput.HasValue)83 {84 formatErrorPolicy.ShowErrorsInFormattedOutput = parameters.showErrorsInFormattedOutput.Value;85 }86 else87 {88 formatErrorPolicy.ShowErrorsInFormattedOutput = this.dataBaseInfo.db.defaultSettingsSection.formatErrorPolicy.ShowErrorsInFormattedOutput;89 }90 91 _errorManager = new FormatErrorManager(formatErrorPolicy);92 }93 94 private void InitializeGroupBy()95 {96 // first check if there is an override from the command line97 if (parameters != null && parameters.groupByParameter != null)98 {99 // get the expression to use100 PSPropertyExpression groupingKeyExpression = parameters.groupByParameter.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as PSPropertyExpression;101 102 // set the label103 string label = null;104 object labelKey = parameters.groupByParameter.GetEntry(FormatParameterDefinitionKeys.LabelEntryKey);105 if (labelKey != AutomationNull.Value)106 {107 label = labelKey as string;108 }109 110 _groupingManager = new GroupingInfoManager();111 _groupingManager.Initialize(groupingKeyExpression, label);112 return;113 }114 115 // check if we have a view to initialize from116 if (this.dataBaseInfo.view != null)117 {118 GroupBy gb = this.dataBaseInfo.view.groupBy;119 if (gb == null)120 {121 return;122 }123 124 if (gb.startGroup == null || gb.startGroup.expression == null)125 {126 return;127 }128 129 PSPropertyExpression ex = this.expressionFactory.CreateFromExpressionToken(gb.startGroup.expression, this.dataBaseInfo.view.loadingInfo);130 131 _groupingManager = new GroupingInfoManager();132 _groupingManager.Initialize(ex, null);133 }134 }135 136 private void InitializeAutoSize()137 {138 // check the autosize flag first139 if (parameters != null && parameters.autosize.HasValue)140 {141 _autosize = parameters.autosize.Value;142 return;143 }144 // check if we have a view with autosize checked145 if (this.dataBaseInfo.view != null && this.dataBaseInfo.view.mainControl != null146 && this.dataBaseInfo.view.mainControl is ControlBody controlBody && controlBody.autosize.HasValue)147 {148 _autosize = controlBody.autosize.Value;149 }150 }151 152 private void InitializeRepeatHeader()153 {154 if (parameters != null)155 {156 _repeatHeader = parameters.repeatHeader;157 }158 }159 160 internal virtual FormatStartData GenerateStartData(PSObject so)161 {162 FormatStartData startFormat = new FormatStartData();163 164 if (_autosize)165 {166 startFormat.autosizeInfo = new AutosizeInfo();167 }168 169 return startFormat;170 }171 172 internal abstract FormatEntryData GeneratePayload(PSObject so, int enumerationLimit);173 174 internal GroupStartData GenerateGroupStartData(PSObject firstObjectInGroup, int enumerationLimit)175 {176 GroupStartData startGroup = new GroupStartData();177 if (_groupingManager == null)178 return startGroup;179 180 object currentGroupingValue = _groupingManager.CurrentGroupingKeyPropertyValue;181 182 if (currentGroupingValue == AutomationNull.Value)183 return startGroup;184 185 PSObject so = PSObjectHelper.AsPSObject(currentGroupingValue);186 187 // we need to determine how to display the group header188 ControlBase control = null;189 TextToken labelTextToken = null;190 if (this.dataBaseInfo.view != null && this.dataBaseInfo.view.groupBy != null)191 {192 if (this.dataBaseInfo.view.groupBy.startGroup != null)193 {194 // NOTE: from the database constraints, only one of the195 // two will be non null196 control = this.dataBaseInfo.view.groupBy.startGroup.control;197 labelTextToken = this.dataBaseInfo.view.groupBy.startGroup.labelTextToken;198 }199 }200 201 startGroup.groupingEntry = new GroupingEntry();202 203 if (control == null)204 {205 // we do not have a control, we auto generate a206 // snippet of complex display using a label207 208 StringFormatError formatErrorObject = null;209 if (_errorManager.DisplayFormatErrorString)210 {211 // we send a format error object down to the formatting calls212 // only if we want to show the formatting error strings213 formatErrorObject = new StringFormatError();214 }215 216 string currentGroupingValueDisplay = PSObjectHelper.SmartToString(so, this.expressionFactory, enumerationLimit, formatErrorObject);217 218 if (formatErrorObject != null && formatErrorObject.exception != null)219 {220 // if we did not have any errors in the expression evaluation221 // we might have errors in the formatting, if present222 _errorManager.LogStringFormatError(formatErrorObject);223 if (_errorManager.DisplayFormatErrorString)224 {225 currentGroupingValueDisplay = _errorManager.FormatErrorString;226 }227 }228 229 FormatEntry fe = new FormatEntry();230 startGroup.groupingEntry.formatValueList.Add(fe);231 232 FormatTextField ftf = new FormatTextField();233 234 // determine what the label should be. If we have a label from the235 // database, let's use it, else fall back to the string provided236 // by the grouping manager237 string label;238 if (labelTextToken != null)239 label = this.dataBaseInfo.db.displayResourceManagerCache.GetTextTokenString(labelTextToken);240 else241 label = _groupingManager.GroupingKeyDisplayName;242 243 ftf.text = StringUtil.Format(FormatAndOut_format_xxx.GroupStartDataIndentedAutoGeneratedLabel, label);244 245 fe.formatValueList.Add(ftf);246 247 FormatPropertyField fpf = new FormatPropertyField();248 249 fpf.propertyValue = currentGroupingValueDisplay;250 fe.formatValueList.Add(fpf);251 }252 else253 {254 // NOTE: we set a max depth to protect ourselves from infinite loops255 const int maxTreeDepth = 50;256 ComplexControlGenerator controlGenerator =257 new ComplexControlGenerator(this.dataBaseInfo.db,258 this.dataBaseInfo.view.loadingInfo,259 this.expressionFactory,260 this.dataBaseInfo.view.formatControlDefinitionHolder.controlDefinitionList,261 this.ErrorManager,262 enumerationLimit,263 this.errorContext);264 265 controlGenerator.GenerateFormatEntries(maxTreeDepth,266 control, firstObjectInGroup, startGroup.groupingEntry.formatValueList);267 }268 269 return startGroup;270 }271 272 /// <summary>273 /// Update the current value of the grouping key.274 /// </summary>275 /// <param name="so">Object to use for the update.</param>276 /// <returns>True if the value of the key changed.</returns>277 internal bool UpdateGroupingKeyValue(PSObject so)278 {279 if (_groupingManager == null)280 return false;281 return _groupingManager.UpdateGroupingKeyValue(so);282 }283 284 internal GroupEndData GenerateGroupEndData()285 {286 return new GroupEndData();287 }288 289 internal bool IsObjectApplicable(Collection<string> typeNames)290 {291 if (dataBaseInfo.view == null)292 return true;293 294 if (typeNames.Count == 0)295 return false;296 297 TypeMatch match = new TypeMatch(expressionFactory, dataBaseInfo.db, typeNames);298 if (match.PerfectMatch(new TypeMatchItem(this, dataBaseInfo.applicableTypes)))299 {300 return true;301 }302 303 bool result = match.BestMatch != null;304 305 // we were unable to find a best match so far..try306 // to get rid of Deserialization prefix and see if a307 // match can be found.308 if (!result)309 {310 Collection<string> typesWithoutPrefix = Deserializer.MaskDeserializationPrefix(typeNames);311 if (typesWithoutPrefix != null)312 {313 result = IsObjectApplicable(typesWithoutPrefix);314 }315 }316 317 return result;318 }319 320 private GroupingInfoManager _groupingManager = null;321 322 protected bool AutoSize323 {324 get { return _autosize; }325 }326 327 private bool _autosize = false;328 329 protected bool RepeatHeader330 {331 get { return _repeatHeader; }332 }333 334 private bool _repeatHeader = false;335 336 protected class DataBaseInfo337 {338 internal TypeInfoDataBase db = null;339 internal ViewDefinition view = null;340 internal AppliesTo applicableTypes = null;341 }342 343 protected TerminatingErrorContext errorContext;344 345 protected FormattingCommandLineParameters parameters;346 347 protected PSPropertyExpressionFactory expressionFactory;348 349 protected DataBaseInfo dataBaseInfo = new DataBaseInfo();350 351 /// <summary>352 /// Builds the raw association list for the given object.353 /// Subclasses override this to provide cmdlet-specific property expansion logic.354 /// </summary>355 /// <param name="so">The object to build the association list for.</param>356 /// <param name="propertyList">The list of properties specified by the user, or null if not specified.</param>357 /// <returns>The raw association list, or null if not applicable.</returns>358 protected virtual List<MshResolvedExpressionParameterAssociation> BuildRawAssociationList(PSObject so, List<MshParameter> propertyList)359 {360 return null;361 }362 363 /// <summary>364 /// Builds the active association list for the given object, with ExcludeProperty filter applied.365 /// </summary>366 /// <param name="so">The object to build the association list for.</param>367 /// <returns>The filtered association list.</returns>368 protected List<MshResolvedExpressionParameterAssociation> BuildActiveAssociationList(PSObject so)369 {370 var propertyList = parameters?.mshParameterList;371 var excludeFilter = parameters?.excludePropertyFilter;372 var rawList = BuildRawAssociationList(so, propertyList);373 return ApplyExcludeFilter(rawList, excludeFilter);374 }375 376 /// <summary>377 /// Applies the ExcludeProperty filter to the given association list.378 /// </summary>379 /// <param name="associationList">The list to filter.</param>380 /// <param name="excludeFilter">The exclude filter to apply.</param>381 /// <returns>The filtered list, or the original list if no filter is specified.</returns>382 internal static List<MshResolvedExpressionParameterAssociation> ApplyExcludeFilter(383 List<MshResolvedExpressionParameterAssociation> associationList,384 PSPropertyExpressionFilter excludeFilter)385 {386 if (associationList is null || excludeFilter is null)387 {388 return associationList;389 }390 391 return associationList392 .Where(item => !excludeFilter.IsMatch(item.ResolvedExpression))393 .ToList();394 }395 396 protected string GetExpressionDisplayValue(PSObject so, int enumerationLimit, PSPropertyExpression ex,397 FieldFormattingDirective directive)398 {399 PSPropertyExpressionResult resolvedExpression;400 return GetExpressionDisplayValue(so, enumerationLimit, ex, directive, out resolvedExpression);401 }402 403 protected string GetExpressionDisplayValue(PSObject so, int enumerationLimit, PSPropertyExpression ex,404 FieldFormattingDirective directive, out PSPropertyExpressionResult expressionResult)405 {406 StringFormatError formatErrorObject = null;407 if (_errorManager.DisplayFormatErrorString)408 {409 // we send a format error object down to the formatting calls410 // only if we want to show the formatting error strings411 formatErrorObject = new StringFormatError();412 }413 414 string retVal = PSObjectHelper.GetExpressionDisplayValue(so, enumerationLimit, ex,415 directive, formatErrorObject, expressionFactory, out expressionResult);416 417 if (expressionResult != null)418 {419 // we obtained a result, check if there is an error420 if (expressionResult.Exception != null)421 {422 _errorManager.LogPSPropertyExpressionFailedResult(expressionResult, so);423 if (_errorManager.DisplayErrorStrings)424 {425 retVal = _errorManager.ErrorString;426 }427 }428 else if (formatErrorObject != null && formatErrorObject.exception != null)429 {430 // if we did not have any errors in the expression evaluation431 // we might have errors in the formatting, if present432 _errorManager.LogStringFormatError(formatErrorObject);433 if (_errorManager.DisplayErrorStrings)434 {435 retVal = _errorManager.FormatErrorString;436 }437 }438 }439 440 return retVal;441 }442 443 protected bool EvaluateDisplayCondition(PSObject so, ExpressionToken conditionToken)444 {445 if (conditionToken == null)446 return true;447 448 PSPropertyExpression ex = this.expressionFactory.CreateFromExpressionToken(conditionToken, this.dataBaseInfo.view.loadingInfo);449 PSPropertyExpressionResult expressionResult;450 bool retVal = DisplayCondition.Evaluate(so, ex, out expressionResult);451 452 if (expressionResult != null && expressionResult.Exception != null)453 {454 _errorManager.LogPSPropertyExpressionFailedResult(expressionResult, so);455 }456 457 return retVal;458 }459 460 internal FormatErrorManager ErrorManager461 {462 get { return _errorManager; }463 }464 465 private FormatErrorManager _errorManager;466 467 #region helpers468 469 protected FormatPropertyField GenerateFormatPropertyField(List<FormatToken> formatTokenList, PSObject so, int enumerationLimit)470 {471 PSPropertyExpressionResult result;472 return GenerateFormatPropertyField(formatTokenList, so, enumerationLimit, out result);473 }474 475 protected FormatPropertyField GenerateFormatPropertyField(List<FormatToken> formatTokenList, PSObject so, int enumerationLimit, out PSPropertyExpressionResult result)476 {477 result = null;478 FormatPropertyField fpf = new FormatPropertyField();479 if (formatTokenList.Count != 0)480 {481 FormatToken token = formatTokenList[0];482 if (token is FieldPropertyToken fpt)483 {484 PSPropertyExpression ex = this.expressionFactory.CreateFromExpressionToken(fpt.expression, this.dataBaseInfo.view.loadingInfo);485 fpf.propertyValue = this.GetExpressionDisplayValue(so, enumerationLimit, ex, fpt.fieldFormattingDirective, out result);486 }487 else if (token is TextToken tt)488 {489 fpf.propertyValue = this.dataBaseInfo.db.displayResourceManagerCache.GetTextTokenString(tt);490 }491 }492 else493 {494 fpf.propertyValue = string.Empty;495 }496 497 return fpf;498 }499 500 #endregion501 }502}503 