Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/CombineQueryExpressions.cs @ 17578

Last change on this file since 17578 was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 7.6 KB
Line 
1//
2// CombineQueryExpressions.cs
3//
4// Modified by Luís Reis <luiscubal@gmail.com> (Copyright (C) 2013)
5//
6// Copyright header of the original version follows:
7//
8// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
9//
10// Permission is hereby granted, free of charge, to any person obtaining a copy of this
11// software and associated documentation files (the "Software"), to deal in the Software
12// without restriction, including without limitation the rights to use, copy, modify, merge,
13// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
14// to whom the Software is furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in all copies or
17// substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
20// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
21// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
22// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24// DEALINGS IN THE SOFTWARE.
25
26using System;
27using System.Linq;
28using ICSharpCode.NRefactory.CSharp;
29using ICSharpCode.NRefactory.PatternMatching;
30
31namespace ICSharpCode.NRefactory.CSharp
32{
33  /// <summary>
34  /// Combines query expressions and removes transparent identifiers.
35  /// </summary>
36  public class CombineQueryExpressions
37  {
38    static readonly InvocationExpression castPattern = new InvocationExpression {
39      Target = new MemberReferenceExpression {
40        Target = new AnyNode("inExpr"),
41        MemberName = "Cast",
42        TypeArguments = { new AnyNode("targetType") }
43      }};
44
45    public string CombineQuery(AstNode node, AstNode rootQuery = null)
46    {
47      if (rootQuery == null) {
48        rootQuery = node;
49      }
50
51      QueryExpression query = node as QueryExpression;
52      if (query != null) {
53        string continuationIdentifier = null;
54
55        foreach (var clause in query.Clauses) {
56          var continuation = clause as QueryContinuationClause;
57          if (continuation != null) {
58            CombineQuery(continuation.PrecedingQuery);
59          }
60
61          var from = clause as QueryFromClause;
62          if (from != null) {
63            continuationIdentifier = CombineQuery(from.Expression, rootQuery);
64          }
65        }
66
67        QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
68        QueryExpression innerQuery = fromClause.Expression as QueryExpression;
69        if (innerQuery != null) {
70          continuationIdentifier = continuationIdentifier ?? ((QueryFromClause)innerQuery.Clauses.First()).Identifier;
71
72          string transparentIdentifier;
73          if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery, continuationIdentifier, out transparentIdentifier)) {
74            RemoveTransparentIdentifierReferences(rootQuery, transparentIdentifier);
75          } else if (fromClause.Type.IsNull) {
76            QueryContinuationClause continuation = new QueryContinuationClause();
77            continuation.PrecedingQuery = innerQuery.Detach();
78            continuation.Identifier = fromClause.Identifier;
79            fromClause.ReplaceWith(continuation);
80          }
81
82          return transparentIdentifier;
83        } else {
84          Match m = castPattern.Match(fromClause.Expression);
85          if (m.Success) {
86            fromClause.Type = m.Get<AstType>("targetType").Single().Detach();
87            fromClause.Expression = m.Get<Expression>("inExpr").Single().Detach();
88          }
89        }
90      }
91
92      return null;
93    }
94
95    static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause {
96      Expression = new AnonymousTypeCreateExpression {
97          Initializers = {
98            new AnyNode("nae1"),
99            new AnyNode("nae2")
100          }
101        }
102      };
103
104    bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery, string continuationIdentifier, out string transparentIdentifier)
105    {
106      transparentIdentifier = fromClause.Identifier;
107
108      Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last());
109      if (!match.Success)
110        return false;
111      QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last();
112      Expression nae1 = match.Get<Expression>("nae1").SingleOrDefault();
113      string nae1Name = ExtractExpressionName(ref nae1);
114      if (nae1Name == null)
115        return false;
116
117      Expression nae2 = match.Get<Expression>("nae2").SingleOrDefault();
118      string nae2Name = ExtractExpressionName(ref nae2);
119      if (nae1Name == null)
120        return false;
121
122      bool introduceLetClause = true;
123      var nae1Identifier = nae1 as IdentifierExpression;
124      var nae2Identifier = nae2 as IdentifierExpression;
125      if (nae1Identifier != null && nae2Identifier != null && nae1Identifier.Identifier == nae1Name && nae2Identifier.Identifier == nae2Name) {
126        introduceLetClause = false;
127      }
128
129      if (nae1Name != continuationIdentifier) {
130        if (nae2Name == continuationIdentifier) {
131          //Members are in reversed order
132          string tempName = nae1Name;
133          Expression tempNae = nae1;
134
135          nae1Name = nae2Name;
136          nae1 = nae2;
137          nae2Name = tempName;
138          nae2 = tempNae;
139        } else {
140          return false;
141        }
142      }
143
144      if (introduceLetClause && innerQuery.Clauses.OfType<QueryFromClause>().Any(from => from.Identifier == nae2Name)) {
145        return false;
146      }
147      if (introduceLetClause && innerQuery.Clauses.OfType<QueryJoinClause>().Any(join => join.JoinIdentifier == nae2Name)) {
148        return false;
149      }
150
151      // from * in (from x in ... select new { x = x, y = expr }) ...
152      // =>
153      // from x in ... let y = expr ...
154      fromClause.Remove();
155      selectClause.Remove();
156      // Move clauses from innerQuery to query
157      QueryClause insertionPos = null;
158      foreach (var clause in innerQuery.Clauses) {
159        query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
160      }
161      if (introduceLetClause) {
162        query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = nae2Name, Expression = nae2.Detach() });
163      }
164      return true;
165    }
166
167    /// <summary>
168    /// Removes all occurrences of transparent identifiers
169    /// </summary>
170    void RemoveTransparentIdentifierReferences(AstNode node, string transparentIdentifier)
171    {
172      foreach (AstNode child in node.Children) {
173        RemoveTransparentIdentifierReferences(child, transparentIdentifier);
174      }
175      MemberReferenceExpression mre = node as MemberReferenceExpression;
176      if (mre != null) {
177        IdentifierExpression ident = mre.Target as IdentifierExpression;
178        if (ident != null && ident.Identifier == transparentIdentifier) {
179          IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName);
180          mre.TypeArguments.MoveTo(newIdent.TypeArguments);
181          newIdent.CopyAnnotationsFrom(mre);
182          newIdent.RemoveAnnotations<PropertyDeclaration>(); // remove the reference to the property of the anonymous type
183          mre.ReplaceWith(newIdent);
184          return;
185        } else if (mre.MemberName == transparentIdentifier) {
186          var newVar = mre.Target.Detach();
187          newVar.CopyAnnotationsFrom(mre);
188          newVar.RemoveAnnotations<PropertyDeclaration>(); // remove the reference to the property of the anonymous type
189          mre.ReplaceWith(newVar);
190          return;
191        }
192      }
193    }
194
195    string ExtractExpressionName(ref Expression expr)
196    {
197      NamedExpression namedExpr = expr as NamedExpression;
198      if (namedExpr != null) {
199        expr = namedExpr.Expression;
200        return namedExpr.Name;
201      }
202
203      IdentifierExpression identifier = expr as IdentifierExpression;
204      if (identifier != null) {
205        return identifier.Identifier;
206      }
207
208      MemberReferenceExpression memberRef = expr as MemberReferenceExpression;
209      if (memberRef != null) {
210        return memberRef.MemberName;
211      }
212
213      return null;
214    }
215  }
216}
Note: See TracBrowser for help on using the repository browser.