code: public TableOperation createWindowAggregate(
  			List<Expression> groupings,
  			List<Expression> aggregates,
  			List<Expression> windowProperties,
  			ResolvedGroupWindow window,
  			TableOperation child) {
  		validateGroupings(groupings);
  		validateAggregates(aggregates);
  		validateWindowProperties(windowProperties, window);
  
  		List<PlannerExpression> convertedGroupings = bridge(groupings);
  		List<PlannerExpression> convertedAggregates = bridge(aggregates);
  		List<PlannerExpression> convertedWindowProperties = bridge(windowProperties);
  
  		TypeInformation[] fieldTypes = concat(
  			convertedGroupings.stream(),
  			convertedAggregates.stream(),
  			convertedWindowProperties.stream()
  		).map(PlannerExpression::resultType)
  			.toArray(TypeInformation[]::new);
  
  		String[] fieldNames = concat(
  			groupings.stream(),
  			aggregates.stream(),
  			windowProperties.stream()
  		).map(expr -> extractName(expr).orElseGet(expr::toString))
  			.toArray(String[]::new);
  
  		TableSchema tableSchema = new TableSchema(fieldNames, fieldTypes);
  
  		return new WindowAggregateTableOperation(
  			groupings,
  			aggregates,
  			windowProperties,
  			window,
  			child,
  			tableSchema);
  	}
docs: Creates a valid {@link WindowAggregateTableOperation} operation.
  
  @param groupings expressions describing grouping key of aggregates
  @param aggregates expressions describing aggregation functions
  @param windowProperties expressions describing window properties
  @param window grouping window of this aggregation
  @param child relational operation on top of which to apply the aggregation
  @return valid window aggregate operation