/// <summary> /// Override this method to restrict the 'all' query inside the filter query. /// </summary> /// <remarks> /// This method is intended to be called from method overrides in subclasses. This method also supports unit-testing scenarios and is not intended to be called from user code. /// Call the Validate method to validate a <see cref="FilterQueryOption"/> instance. /// </remarks> /// <param name="allNode"></param> /// <param name="settings"></param> public virtual void ValidateAllNode(AllNode allNode, ODataValidationSettings settings) { if (allNode == null) { throw Error.ArgumentNull("allNode"); } if (settings == null) { throw Error.ArgumentNull("settings"); } EnterLambda(settings); try { ValidateQueryNode(allNode.Source, settings); ValidateQueryNode(allNode.Body, settings); } finally { ExitLambda(); } }
/// <summary> /// Creates an AnyNode or an AllNode from the given /// </summary> /// <param name="state">State of binding.</param> /// <param name="parent">Parent node to the lambda.</param> /// <param name="lambdaExpression">Bound Lambda expression.</param> /// <param name="newRangeVariable">The new range variable being added by this lambda node.</param> /// <param name="queryTokenKind">Token kind.</param> /// <returns>A new LambdaNode bound to metadata.</returns> internal static LambdaNode CreateLambdaNode( BindingState state, CollectionNode parent, SingleValueNode lambdaExpression, RangeVariable newRangeVariable, QueryTokenKind queryTokenKind) { LambdaNode lambdaNode; if (queryTokenKind == QueryTokenKind.Any) { lambdaNode = new AnyNode(new Collection <RangeVariable>(state.RangeVariables.ToList()), newRangeVariable) { Body = lambdaExpression, Source = parent, }; } else { Debug.Assert(queryTokenKind == QueryTokenKind.All, "LambdaQueryNodes must be Any or All only."); lambdaNode = new AllNode(new Collection <RangeVariable>(state.RangeVariables.ToList()), newRangeVariable) { Body = lambdaExpression, Source = parent, }; } return(lambdaNode); }
private string BindAllNode(AllNode allNode, ICollection <BinderNode> nodes) { string innerQuery = "not exists ( from " + Bind(allNode.Source, nodes) + " " + allNode.RangeVariables.First().Name; innerQuery += " where NOT(" + Bind(allNode.Body, nodes) + ")"; return(innerQuery + ")"); }
/// <summary> /// Override this method to restrict the 'all' query inside the filter query. /// </summary> /// <remarks> /// This method is intended to be called from method overrides in subclasses. This method also supports unit-testing scenarios and is not intended to be called from user code. /// Call the Validate method to validate a <see cref="FilterQueryOption"/> instance. /// </remarks> /// <param name="allNode"></param> /// <param name="settings"></param> public virtual void ValidateAllNode(AllNode allNode, ODataValidationSettings settings) { if (allNode == null) { throw Error.ArgumentNull("allNode"); } if (settings == null) { throw Error.ArgumentNull("settings"); } EnterLambda(settings); try { ValidateQueryNode(allNode.Source, settings); ValidateQueryNode(allNode.Body, settings); } finally { ExitLambda(); } }
private string BindAllNode(AllNode allNode) { string innerQuery = "not exists ( from " + Bind(allNode.Source) + " " + allNode.RangeVariables.First().Name; innerQuery += " where NOT(" + Bind(allNode.Body) + ")"; return(innerQuery + ")"); }
private void TestLambda <TLambda, TParam, TReturn>(CollectionNode source, string parameterName, SingleValueNode body, Expression <Func <TParam, TReturn> > expectedExpression) where TLambda : LambdaNode { EntityRangeVariable currentRangeVariable = null; if (parameterName != null) { currentRangeVariable = new EntityRangeVariable(parameterName, new EdmEntityTypeReference(this.customerEdmType, false), this.entitySet); currentRangeVariable.SetAnnotation(Expression.Parameter(typeof(TParam), parameterName)); } LambdaNode node; if (typeof(TLambda) == typeof(AnyNode)) { node = new AnyNode(new Collection <RangeVariable>(), currentRangeVariable); } else { node = new AllNode(new Collection <RangeVariable>(), currentRangeVariable); } node.Body = body; node.Source = source; var result = this.testSubject.TranslateNode(node); CompareExpressions(expectedExpression.Body, result); }
/// <summary> /// Translates a <see cref="AllNode"/> into a corresponding <see cref="String"/>. /// </summary> /// <param name="node">The node to translate.</param> /// <returns>The translated String.</returns> public override String Visit(AllNode node) { ExceptionUtils.CheckArgumentNotNull(node, "node"); String result = String.Concat(this.TranslateNode(node.Source), ExpressionConstants.SymbolForwardSlash, ExpressionConstants.KeywordAll, ExpressionConstants.SymbolOpenParen, node.CurrentRangeVariable.Name, ":", this.TranslateNode(node.Body), ExpressionConstants.SymbolClosedParen); return result; }
/// <summary> /// Binds a <see cref="AllNode"/> to create a LINQ <see cref="Expression"/> that /// represents the semantics of the <see cref="AllNode"/>. /// </summary> /// <param name="allNode">The node to bind.</param> /// <returns>The LINQ <see cref="Expression"/> created.</returns> public virtual Expression BindAllNode(AllNode allNode) { ParameterExpression allIt = HandleLambdaParameters(allNode.RangeVariables); Expression source; Contract.Assert(allNode.Source != null); source = Bind(allNode.Source); Expression body = source; Contract.Assert(allNode.Body != null); body = Bind(allNode.Body); body = ApplyNullPropagationForFilterBody(body); body = Expression.Lambda(body, allIt); Expression all = All(source, body); ExitLamdbaScope(); if (QuerySettings.HandleNullPropagation == HandleNullPropagationOption.True && IsNullable(source.Type)) { // IFF(source == null) null; else Any(body); all = ToNullable(all); return(Expression.Condition( test: Expression.Equal(source, NullConstant), ifTrue: Expression.Constant(null, all.Type), ifFalse: all)); } else { return(all); } }
static string BindAllNode(AllNode allNode, List <DbParameter> pars, DbUtility dbUtility) { string innerQuery = "not exists ( from " + Bind(allNode.Source, pars, dbUtility) + " " + allNode.RangeVariables.First().Name; innerQuery += " where NOT(" + Bind(allNode.Body, pars, dbUtility) + ")"; return(innerQuery + ")"); }
/// <summary> /// Translates a V3 AllNode to V4 AllNode. Visiting child nodes need to be marked dynamic /// because method overloading for overridden methods works differently than usual. /// </summary> /// <param name="nodeIn">V3 AllNode</param> /// <returns>V4 AllNode</returns> public override QueryNode Visit(Data.OData.Query.SemanticAst.AllNode nodeIn) { IEnumerable<RangeVariable> translated = nodeIn.RangeVariables.Select(r => TranslateRangeVariable(r)); AllNode ret = new AllNode(new Collection<RangeVariable>(translated.ToList()), TranslateRangeVariable(nodeIn.CurrentRangeVariable)); ret.Source = (CollectionNode)nodeIn.Source.Accept(this); ret.Body = (SingleValueNode)nodeIn.Body.Accept(this); return ret; }
public override int Visit(AllNode nodeIn) { var sourceNode = (CollectionNavigationNode)nodeIn.Source; int h1 = sourceNode.NavigationProperty.Name.GetHashCode(); int h2 = nameof(Enumerable.All).GetHashCode(); int h3 = TranslateNode(nodeIn.Body); return(CombineHashCodes(h1, h2, h3)); }
public void KindShouldBeAllNode() { EntityRangeVariable rangeVariable = (EntityRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection<RangeVariable> rangeVariables = new Collection<RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); allNode.InternalKind.Should().Be(InternalQueryNodeKind.All); }
public void TypeReferenceShouldBeBoolean() { EntityRangeVariable rangeVariable = (EntityRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection<RangeVariable> rangeVariables = new Collection<RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); allNode.TypeReference.FullName().Should().Be("Edm.Boolean"); }
protected virtual QueryNode VisitAll(AllNode node, AzureQueryOptimizerState state) { QueryNode queryNode1 = this.Visit(node.SourceNode, state); QueryNode queryNode2 = this.Visit(node.PredicateNode, state); if (queryNode2.NodeType == QueryNodeType.MatchAll) { return((QueryNode) new AllNode(queryNode1, queryNode2)); } return((QueryNode) new AllNode(this.VisitAnd(new AndNode(queryNode1, queryNode2), state), (QueryNode) new MatchAllNode())); }
public void KindShouldBeAllNode() { ResourceRangeVariable rangeVariable = (ResourceRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection <RangeVariable> rangeVariables = new Collection <RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); Assert.Equal(InternalQueryNodeKind.All, allNode.InternalKind); }
public void TypeReferenceShouldBeBoolean() { ResourceRangeVariable rangeVariable = (ResourceRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection <RangeVariable> rangeVariables = new Collection <RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); Assert.Equal("Edm.Boolean", allNode.TypeReference.FullName()); }
protected virtual QueryNode VisitAll(AllNode node, ElasticSearchQueryOptimizerState state) { var node2 = Visit(node.SourceNode, state); var node3 = Visit(node.PredicateNode, state); if (node3.NodeType == QueryNodeType.MatchAll) { return(new AllNode(node2, node3)); } return(new AllNode(VisitAnd(new AndNode(node2, node3), state), new MatchAllNode())); }
public void TypeReferenceShouldBeBoolean() { EntityRangeVariable rangeVariable = (EntityRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection <RangeVariable> rangeVariables = new Collection <RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); allNode.TypeReference.FullName().Should().Be("Edm.Boolean"); }
public void KindShouldBeAllNode() { EntityRangeVariable rangeVariable = (EntityRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection <RangeVariable> rangeVariables = new Collection <RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); allNode.InternalKind.Should().Be(InternalQueryNodeKind.All); }
public void RangeVariableShouldBeSetCorrectly() { EntityRangeVariable rangeVariable = (EntityRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection<RangeVariable> rangeVariables = new Collection<RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); allNode.CurrentRangeVariable.Name.Should().Be(ExpressionConstants.It); allNode.CurrentRangeVariable.Kind.Should().Be(RangeVariableKind.Entity); allNode.CurrentRangeVariable.TypeReference.FullName().Should().Be(HardCodedTestModel.GetDogTypeReference().FullName()); }
public void RangeVariableShouldBeSetCorrectly() { ResourceRangeVariable rangeVariable = (ResourceRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection <RangeVariable> rangeVariables = new Collection <RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); Assert.Equal(ExpressionConstants.It, allNode.CurrentRangeVariable.Name); Assert.Equal(RangeVariableKind.Resource, allNode.CurrentRangeVariable.Kind); Assert.Equal(HardCodedTestModel.GetDogTypeReference().FullName(), allNode.CurrentRangeVariable.TypeReference.FullName()); }
public override void ValidateAllNode(AllNode allNode, ODataValidationSettings settings) { // NOT WORKING!!! foreach (var node in allNode.RangeVariables) { // if (node.Kind == ) if (node != null) { } } base.ValidateAllNode(allNode, settings); }
public void RangeVariableShouldBeSetCorrectly() { EntityRangeVariable rangeVariable = (EntityRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection <RangeVariable> rangeVariables = new Collection <RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); allNode.CurrentRangeVariable.Name.Should().Be(ExpressionConstants.It); allNode.CurrentRangeVariable.Kind.Should().Be(RangeVariableKind.Entity); allNode.CurrentRangeVariable.TypeReference.FullName().Should().Be(HardCodedTestModel.GetDogTypeReference().FullName()); }
public void RangeVariablesShouldBeSetCorrectly() { EntityRangeVariable rangeVariable = (EntityRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection<RangeVariable> rangeVariables = new Collection<RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); allNode.RangeVariables.Count.Should().Be(1); allNode.RangeVariables[0].Name.Should().Be(ExpressionConstants.It); allNode.RangeVariables[0].Kind.Should().Be(RangeVariableKind.Entity); EntityRangeVariable returnedRangeVariable = (EntityRangeVariable)allNode.RangeVariables[0]; returnedRangeVariable.NavigationSource.Should().Be(HardCodedTestModel.GetDogsSet()); }
public void RangeVariablesShouldBeSetCorrectly() { EntityRangeVariable rangeVariable = (EntityRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection <RangeVariable> rangeVariables = new Collection <RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); allNode.RangeVariables.Count.Should().Be(1); allNode.RangeVariables[0].Name.Should().Be(ExpressionConstants.It); allNode.RangeVariables[0].Kind.Should().Be(RangeVariableKind.Entity); EntityRangeVariable returnedRangeVariable = (EntityRangeVariable)allNode.RangeVariables[0]; returnedRangeVariable.NavigationSource.Should().Be(HardCodedTestModel.GetDogsSet()); }
public void RangeVariablesShouldBeSetCorrectly() { ResourceRangeVariable rangeVariable = (ResourceRangeVariable)NodeFactory.CreateImplicitRangeVariable(HardCodedTestModel.GetDogTypeReference(), HardCodedTestModel.GetDogsSet()); Collection <RangeVariable> rangeVariables = new Collection <RangeVariable> { rangeVariable }; AllNode allNode = new AllNode(rangeVariables, rangeVariable); RangeVariable rangeVar = Assert.Single(allNode.RangeVariables); Assert.Equal(ExpressionConstants.It, rangeVar.Name); Assert.Equal(RangeVariableKind.Resource, rangeVar.Kind); ResourceRangeVariable returnedRangeVariable = Assert.IsType <ResourceRangeVariable>(rangeVar); Assert.Same(HardCodedTestModel.GetDogsSet(), returnedRangeVariable.NavigationSource); }
public override QueryNode Visit(AllNode nodeIn) { var body = (SingleValueNode)Visit(nodeIn.Body); CollectionNode?source = nodeIn.Source == null ? null : (CollectionNode)Visit(nodeIn.Source); if (nodeIn.Body != body || nodeIn.Source != source) { nodeIn = new AllNode(nodeIn.RangeVariables, nodeIn.CurrentRangeVariable) { Body = body, Source = source } } ; return(nodeIn); }
/// <summary> /// Override this method to restrict the 'all' query inside the filter query /// </summary> /// <param name="allNode"></param> /// <param name="settings"></param> public virtual void ValidateAllNode(AllNode allNode, ODataValidationSettings settings) { if (allNode == null) { throw Error.ArgumentNull("allNode"); } if (settings == null) { throw Error.ArgumentNull("settings"); } ValidateQueryNode(allNode.Source, settings); ValidateQueryNode(allNode.Body, settings); }
/// <summary> /// Override this method to restrict the 'all' query inside the filter query /// </summary> /// <param name="allNode"></param> /// <param name="settings"></param> public virtual void ValidateAllNode(AllNode allNode, ODataValidationSettings settings) { if (allNode == null) { throw Error.ArgumentNull("allNode"); } if (settings == null) { throw Error.ArgumentNull("settings"); } ValidateQueryNode(allNode.Source, settings); ValidateQueryNode(allNode.Body, settings); }
/// <summary> /// Translate an AllNode. /// </summary> /// <param name="nodeIn">The node to be translated.</param> /// <returns>The translated node.</returns> public override QueryNode Visit(AllNode nodeIn) { AllNode allNode = new AllNode(nodeIn.RangeVariables, nodeIn.CurrentRangeVariable); if (nodeIn.Source != null) { allNode.Source = (CollectionNode)nodeIn.Source.Accept(this); } if (nodeIn.Body != null) { allNode.Body = (SingleValueNode)nodeIn.Body.Accept(this); } return(allNode); }
/// <summary> /// Override this method to restrict the 'all' query inside the filter query. /// </summary> /// <remarks> /// This method is intended to be called from method overrides in subclasses. This method also supports unit-testing scenarios and is not intended to be called from user code. /// Call the Validate method to validate a <see cref="FilterQueryOption"/> instance. /// </remarks> /// <param name="allNode"></param> /// <param name="settings"></param> protected virtual void ValidateAllNode(AllNode allNode, ODataValidationSettings settings) { Contract.Assert(allNode != null); ValidateFunction("all", settings); EnterLambda(settings); try { ValidateQueryNode(allNode.Source, settings); ValidateQueryNode(allNode.Body, settings); } finally { ExitLambda(); } }
/// <summary> /// Visit an AllNode /// </summary> /// <param name="nodeIn">The node to visit</param> ///// <returns>The translated expression</returns> public override Expression Visit(AllNode nodeIn) { var instanceType = EdmClrTypeUtils.GetInstanceType(nodeIn.RangeVariables[0].TypeReference); ParameterExpression parameter = Expression.Parameter(instanceType, nodeIn.RangeVariables[0].Name); NodeToExpressionTranslator nodeToExpressionTranslator = new NodeToExpressionTranslator() { ImplicitVariableParameterExpression = parameter, UriParser = this.UriParser, DataSource = this.DataSource, }; Expression conditionExpression = nodeToExpressionTranslator.TranslateNode(nodeIn.Body); Expression rootExpression = this.TranslateNode(nodeIn.Source); return(Expression.Call( typeof(Enumerable), "All", new Type[] { instanceType }, rootExpression, Expression.Lambda(conditionExpression, parameter))); }
/// <summary> /// Writes all node to string. /// </summary> /// <param name="node">Node to write to string</param> /// <returns>String representation of node.</returns> private static string ToString(AllNode node) { if (node != null) { return(tabHelper.Prefix + "AllNode" + tabHelper.Indent(() => { string text = tabHelper.Prefix + "Type Reference = " + node.TypeReference + tabHelper.Prefix + "Body = " + ToString(node.Body) + tabHelper.Prefix + "Source = " + ToString(node.Source) + tabHelper.Prefix + "Parameters = "; for (int i = 0; i < node.RangeVariables.Count(); ++i) { text += ToStringParameter(node.RangeVariables.ElementAt(i)); } return text; })); } return(String.Empty); }
/// <summary> /// Compares all query nodes. /// </summary> /// <param name="left">Left side of comparison</param> /// <param name="right">Right side of comparison</param> /// <returns>True if equal, otherwise false</returns> private bool Compare(AllNode left, AllNode right) { if (left.TypeReference != right.TypeReference) { return(false); } if (!this.Compare(left.Body, right.Body)) { return(false); } if (left.RangeVariables.Count() != right.RangeVariables.Count()) { return(false); } for (int i = 0; i < left.RangeVariables.Count(); ++i) { if (!this.CompareParameters(left.RangeVariables.ElementAt(i), right.RangeVariables.ElementAt(i))) { return(false); } } return(this.Compare(left.Source, right.Source)); }
public override void ValidateAllNode(AllNode allQueryNode, ODataValidationSettings settings) { IncrementCount("ValidateAllQueryNode"); base.ValidateAllNode(allQueryNode, settings); }
/// <summary> /// Compares all query nodes. /// </summary> /// <param name="left">Left side of comparison</param> /// <param name="right">Right side of comparison</param> /// <returns>True if equal, otherwise false</returns> private bool Compare(AllNode left, AllNode right) { if (left.TypeReference != right.TypeReference) return false; if (!this.Compare(left.Body, right.Body)) return false; if (left.RangeVariables.Count() != right.RangeVariables.Count()) return false; for (int i = 0; i < left.RangeVariables.Count(); ++ i ) { if (!this.CompareParameters(left.RangeVariables.ElementAt(i), right.RangeVariables.ElementAt(i))) return false; } return this.Compare(left.Source, right.Source); }
private bool Visit(AllNode node1, AllNode node2) { return(node1.TypeReference.IsEqual(node2.TypeReference) && Compare(node1.Source, node2.Source) && Compare(node1.Body, node2.Body)); }
/// <summary> /// Creates an AnyNode or an AllNode from the given /// </summary> /// <param name="state">State of binding.</param> /// <param name="parent">Parent node to the lambda.</param> /// <param name="lambdaExpression">Bound Lambda expression.</param> /// <param name="newRangeVariable">The new range variable being added by this lambda node.</param> /// <param name="queryTokenKind">Token kind.</param> /// <returns>A new LambdaNode bound to metadata.</returns> internal static LambdaNode CreateLambdaNode( BindingState state, CollectionNode parent, SingleValueNode lambdaExpression, RangeVariable newRangeVariable, QueryTokenKind queryTokenKind) { LambdaNode lambdaNode; if (queryTokenKind == QueryTokenKind.Any) { lambdaNode = new AnyNode(new Collection<RangeVariable>(state.RangeVariables.ToList()), newRangeVariable) { Body = lambdaExpression, Source = parent, }; } else { Debug.Assert(queryTokenKind == QueryTokenKind.All, "LambdaQueryNodes must be Any or All only."); lambdaNode = new AllNode(new Collection<RangeVariable>(state.RangeVariables.ToList()), newRangeVariable) { Body = lambdaExpression, Source = parent, }; } return lambdaNode; }
private string BindAllNode(AllNode allNode) { string innerQuery = "not exists ( from " + Bind(allNode.Source) + " " + allNode.RangeVariables.First().Name; innerQuery += " where NOT(" + Bind(allNode.Body) + ")"; return innerQuery + ")"; }
/// <summary> /// Translates a <see cref="AllNode"/> into a corresponding <see cref="string"/>. /// </summary> /// <param name="node">The node to translate.</param> /// <returns>The translated string.</returns> public override string Visit(AllNode node) { return(string.Concat(Constants.Delimiter, this.TranslateNode(node.Source, true), Constants.Delimiter, this.TranslateNode(node.Body))); }
/// <summary> /// Writes all node to string. /// </summary> /// <param name="node">Node to write to string</param> /// <returns>String representation of node.</returns> private static string ToString(AllNode node) { if (node != null) { return tabHelper.Prefix + "AllNode" + tabHelper.Indent(() => { string text = tabHelper.Prefix + "Type Reference = " + node.TypeReference + tabHelper.Prefix + "Body = " + ToString(node.Body) + tabHelper.Prefix + "Source = " + ToString(node.Source) + tabHelper.Prefix + "Parameters = "; for (int i = 0; i < node.RangeVariables.Count(); ++i) { text += ToStringParameter(node.RangeVariables.ElementAt(i)); } return text; }); } return String.Empty; }