1 //---------------------------------------------------------------------
2 // <copyright file="CellCreator.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 
10 using System.Data.Common.Utils;
11 using System.Data.Mapping.ViewGeneration.Structures;
12 using System.Collections.Generic;
13 using System.Data.Mapping.ViewGeneration.Utils;
14 using System.Diagnostics;
15 using System.Data.Metadata.Edm;
16 using System.Linq;
17 
18 namespace System.Data.Mapping.ViewGeneration
19 {
20     /// <summary>
21     /// A class that handles creation of cells from the meta data information.
22     /// </summary>
23     internal class CellCreator : InternalBase
24     {
25 
26         #region Constructors
27         // effects: Creates a cell creator object for an entity container's
28         // mappings (specified in "maps")
CellCreator(StorageEntityContainerMapping containerMapping)29         internal CellCreator(StorageEntityContainerMapping containerMapping)
30         {
31             m_containerMapping = containerMapping;
32             m_identifiers = new CqlIdentifiers();
33         }
34         #endregion
35 
36         #region Fields
37         // The mappings from the metadata for different containers
38         private StorageEntityContainerMapping m_containerMapping;
39         private int m_currentCellNumber;
40         private CqlIdentifiers m_identifiers;
41         // Keep track of all the identifiers to prevent clashes with _from0,
42         // _from1, T, T1, etc
43         // Keep track of names of
44         // * Entity Containers
45         // * Extent names
46         // * Entity Types
47         // * Complex Types
48         // * Properties
49         // * Roles
50         #endregion
51 
52         #region Properties
53         // effects: Returns the set of identifiers used in this
54         internal CqlIdentifiers Identifiers
55         {
56             get { return m_identifiers; }
57         }
58         #endregion
59 
60         #region External methods
61         // effects: Generates the cells for all the entity containers
62         // specified in this. The generated cells are geared for query view generation
GenerateCells(ConfigViewGenerator config)63         internal List<Cell> GenerateCells(ConfigViewGenerator config)
64         {
65             List<Cell> cells = new List<Cell>();
66 
67             // Get the cells from the entity container metadata
68             ExtractCells(cells);
69 
70             ExpandCells(cells);
71 
72             // Get the identifiers from the cells
73             m_identifiers.AddIdentifier(m_containerMapping.EdmEntityContainer.Name);
74             m_identifiers.AddIdentifier(m_containerMapping.StorageEntityContainer.Name);
75             foreach (Cell cell in cells)
76             {
77                 cell.GetIdentifiers(m_identifiers);
78             }
79 
80             return cells;
81         }
82         #endregion
83 
84         #region Private Methods
85         /// <summary>
86         /// Boolean members have a closed domain and are enumerated when domains are established i.e. (T, F) instead of (notNull).
87         /// Query Rewriting is exercised over every domain of the condition member. If the member contains not_null condition
88         /// for example, it cannot generate a view for partitions (member=T), (Member=F). For this reason we need to expand the cells
89         /// in a predefined situation (below) to include sub-fragments mapping individual elements of the closed domain.
90         /// Enums (a planned feature) need to be handled in a similar fashion.
91         ///
92         /// Find booleans that are projected with a not_null condition
93         /// Expand ALL cells where they are projected. Why? See Unit Test case NullabilityConditionOnBoolean5.es
94         /// Validation will fail because it will not be able to validate rewritings for partitions on the 'other' cells.
95         /// </summary>
ExpandCells(List<Cell> cells)96         private void ExpandCells(List<Cell> cells)
97         {
98             var sSideMembersToBeExpanded = new Set<MemberPath>();
99 
100             foreach (Cell cell in cells)
101             {
102                 //Find Projected members that are Boolean AND are mentioned in the Where clause with not_null condition
103                 foreach (var memberToExpand in cell.SQuery.GetProjectedMembers()
104                                             .Where(member => IsBooleanMember(member))
105                                             .Where(boolMember => cell.SQuery.GetConjunctsFromWhereClause()
106                                                                     .Where(restriction => restriction.Domain.Values.Contains(Constant.NotNull))
107                                                                     .Select(restriction => restriction.RestrictedMemberSlot.MemberPath).Contains(boolMember)))
108                 {
109                     sSideMembersToBeExpanded.Add(memberToExpand);
110                 }
111             }
112 
113             //Foreach s-side members, find all c-side members it is mapped to
114             //  We need these because we need to expand all cells where the boolean candidate is projected or mapped member is projected, e.g:
115             //   (1) C[id, cdisc] WHERE d=true   <=>   T1[id, sdisc] WHERE sdisc=NOTNULL
116             //   (2) C[id, cdisc] WHERE d=false  <=>   T2[id, sdisc]
117             //  Here we need to know that because of T1.sdisc, we need to expand T2.sdisc.
118             //  This is done by tracking cdisc, and then seeing in cell 2 that it is mapped to T2.sdisc
119 
120             var cSideMembersForSSideExpansionCandidates = new Dictionary<MemberPath, Set<MemberPath>>();
121             foreach (Cell cell in cells)
122             {
123                 foreach (var sSideMemberToExpand in sSideMembersToBeExpanded)
124                 {
125                     var cSideMembers = cell.SQuery.GetProjectedPositions(sSideMemberToExpand).Select(pos => ((MemberProjectedSlot)cell.CQuery.ProjectedSlotAt(pos)).MemberPath);
126 
127                     Set<MemberPath> cSidePaths = null;
128                     if (!cSideMembersForSSideExpansionCandidates.TryGetValue(sSideMemberToExpand, out cSidePaths))
129                     {
130                         cSidePaths = new Set<MemberPath>();
131                         cSideMembersForSSideExpansionCandidates[sSideMemberToExpand] = cSidePaths;
132                     }
133 
134                     cSidePaths.AddRange(cSideMembers);
135                 }
136             }
137 
138             // Expand cells that project members collected earlier with T/F conditiions
139             foreach (Cell cell in cells.ToArray())
140             {
141                 //Each member gets its own expansion. Including multiple condition candidates in one SQuery
142                 // "... <=> T[..] WHERE a=notnull AND b=notnull" means a and b get their own independent expansions
143                 // Note: this is not a cross-product
144                 foreach (var memberToExpand in sSideMembersToBeExpanded)
145                 {
146                     var mappedCSideMembers = cSideMembersForSSideExpansionCandidates[memberToExpand];
147 
148                     //Check if member is projected in this cell.
149                     if (cell.SQuery.GetProjectedMembers().Contains(memberToExpand))
150                     {
151                         // Creationg additional cel can fail when the condition to be appended contradicts existing condition in the CellQuery
152                         // We don't add contradictions because they seem to cause unrelated problems in subsequent validation routines
153                         Cell resultCell = null;
154                         if (TryCreateAdditionalCellWithCondition(cell, memberToExpand, true  /*condition value*/, ViewTarget.UpdateView /*s-side member*/, out resultCell))
155                         {
156                             cells.Add(resultCell);
157                         }
158                         if (TryCreateAdditionalCellWithCondition(cell, memberToExpand, false /*condition value*/, ViewTarget.UpdateView /*s-side member*/, out resultCell))
159                         {
160                             cells.Add(resultCell);
161                         }
162                     }
163                     else
164                     {  //If the s-side member is not projected, see if the mapped C-side member(s) is projected
165                         foreach (var cMemberToExpand in cell.CQuery.GetProjectedMembers().Intersect(mappedCSideMembers))
166                         {
167                             Cell resultCell = null;
168                             if (TryCreateAdditionalCellWithCondition(cell, cMemberToExpand, true  /*condition value*/, ViewTarget.QueryView /*c-side member*/, out resultCell))
169                             {
170                                 cells.Add(resultCell);
171                             }
172 
173                             if (TryCreateAdditionalCellWithCondition(cell, cMemberToExpand, false /*condition value*/, ViewTarget.QueryView /*c-side member*/, out resultCell))
174                             {
175                                 cells.Add(resultCell);
176                             }
177                         }
178                     }
179                 }
180             }
181 
182         }
183 
184         /// <summary>
185         /// Given a cell, a member and a boolean condition on that member, creates additional cell
186         /// which with the specified restriction on the member in addition to original condition.
187         /// e.i conjunction of original condition AND member in newCondition
188         ///
189         /// Creation fails when the original condition contradicts new boolean condition
190         ///
191         /// ViewTarget tells whether MemberPath is in Cquery or SQuery
192         /// </summary>
TryCreateAdditionalCellWithCondition(Cell originalCell, MemberPath memberToExpand, bool conditionValue, ViewTarget viewTarget, out Cell result)193         private bool TryCreateAdditionalCellWithCondition(Cell originalCell, MemberPath memberToExpand, bool conditionValue, ViewTarget viewTarget, out Cell result)
194         {
195             Debug.Assert(originalCell != null);
196             Debug.Assert(memberToExpand != null);
197             result = null;
198 
199             //Create required structures
200             MemberPath leftExtent = originalCell.GetLeftQuery(viewTarget).SourceExtentMemberPath;
201             MemberPath rightExtent = originalCell.GetRightQuery(viewTarget).SourceExtentMemberPath;
202 
203             //Now for the given left-side projected member, find corresponding right-side member that it is mapped to
204             int indexOfBooLMemberInProjection = originalCell.GetLeftQuery(viewTarget).GetProjectedMembers().TakeWhile(path => !path.Equals(memberToExpand)).Count();
205             MemberProjectedSlot rightConditionMemberSlot = ((MemberProjectedSlot)originalCell.GetRightQuery(viewTarget).ProjectedSlotAt(indexOfBooLMemberInProjection));
206             MemberPath rightSidePath = rightConditionMemberSlot.MemberPath;
207 
208             List<ProjectedSlot> leftSlots = new List<ProjectedSlot>();
209             List<ProjectedSlot> rightSlots = new List<ProjectedSlot>();
210 
211             //Check for impossible conditions (otehrwise we get inaccurate pre-validation errors)
212             ScalarConstant negatedCondition = new ScalarConstant(!conditionValue);
213 
214             if (originalCell.GetLeftQuery(viewTarget).Conditions
215                     .Where(restriction => restriction.RestrictedMemberSlot.MemberPath.Equals(memberToExpand))
216                     .Where(restriction => restriction.Domain.Values.Contains(negatedCondition)).Any()
217                 || originalCell.GetRightQuery(viewTarget).Conditions
218                     .Where(restriction => restriction.RestrictedMemberSlot.MemberPath.Equals(rightSidePath))
219                     .Where(restriction => restriction.Domain.Values.Contains(negatedCondition)).Any())
220             {
221                 return false;
222             }
223             //End check
224 
225             //Create Projected Slots
226             // Map all slots in original cell (not just keys) because some may be required (non nullable and no default)
227             // and others may have not_null condition so MUST be projected. Rely on the user doing the right thing, otherwise
228             // they will get the error message anyway
229             for (int i = 0; i < originalCell.GetLeftQuery(viewTarget).NumProjectedSlots; i++)
230             {
231                 leftSlots.Add(originalCell.GetLeftQuery(viewTarget).ProjectedSlotAt(i));
232             }
233 
234             for (int i = 0; i < originalCell.GetRightQuery(viewTarget).NumProjectedSlots; i++)
235             {
236                 rightSlots.Add(originalCell.GetRightQuery(viewTarget).ProjectedSlotAt(i));
237             }
238 
239             //Create condition boolena expressions
240             BoolExpression leftQueryWhereClause = BoolExpression.CreateLiteral(new ScalarRestriction(memberToExpand, new ScalarConstant(conditionValue)), null);
241             leftQueryWhereClause = BoolExpression.CreateAnd(originalCell.GetLeftQuery(viewTarget).WhereClause, leftQueryWhereClause);
242 
243             BoolExpression rightQueryWhereClause = BoolExpression.CreateLiteral(new ScalarRestriction(rightSidePath, new ScalarConstant(conditionValue)), null);
244             rightQueryWhereClause = BoolExpression.CreateAnd(originalCell.GetRightQuery(viewTarget).WhereClause, rightQueryWhereClause);
245 
246             //Create additional Cells
247             CellQuery rightQuery = new CellQuery(rightSlots, rightQueryWhereClause, rightExtent, originalCell.GetRightQuery(viewTarget).SelectDistinctFlag);
248             CellQuery leftQuery = new CellQuery(leftSlots, leftQueryWhereClause, leftExtent, originalCell.GetLeftQuery(viewTarget).SelectDistinctFlag);
249 
250             Cell newCell;
251             if (viewTarget == ViewTarget.UpdateView)
252             {
253                 newCell = Cell.CreateCS(rightQuery, leftQuery, originalCell.CellLabel, m_currentCellNumber);
254             }
255             else
256             {
257                 newCell = Cell.CreateCS(leftQuery, rightQuery, originalCell.CellLabel, m_currentCellNumber);
258             }
259 
260             m_currentCellNumber++;
261             result = newCell;
262             return true;
263         }
264 
265         // effects: Given the metadata information for a container in
266         // containerMap, generate the cells for it and modify cells to
267         // contain the newly-generated cells
ExtractCells(List<Cell> cells)268         private void ExtractCells(List<Cell> cells)
269         {
270             // extract entity mappings, i.e., for CPerson1, COrder1, etc
271             foreach (StorageSetMapping extentMap in m_containerMapping.AllSetMaps)
272             {
273 
274                 // Get each type map in an entity set mapping, i.e., for
275                 // CPerson, CCustomer, etc in CPerson1
276                 foreach (StorageTypeMapping typeMap in extentMap.TypeMappings)
277                 {
278 
279                     StorageEntityTypeMapping entityTypeMap = typeMap as StorageEntityTypeMapping;
280                     Debug.Assert(entityTypeMap != null ||
281                                  typeMap is StorageAssociationTypeMapping, "Invalid typemap");
282 
283                     // A set for all the types in this type mapping
284                     Set<EdmType> allTypes = new Set<EdmType>();
285 
286                     if (entityTypeMap != null)
287                     {
288                         // Gather a set of all explicit types for an entity
289                         // type mapping in allTypes. Note that we do not have
290                         // subtyping in association sets
291                         allTypes.AddRange(entityTypeMap.Types);
292                         foreach (EdmType type in entityTypeMap.IsOfTypes)
293                         {
294                             IEnumerable<EdmType> typeAndSubTypes = MetadataHelper.GetTypeAndSubtypesOf(type, m_containerMapping.StorageMappingItemCollection.EdmItemCollection, false /*includeAbstractTypes*/);
295                             allTypes.AddRange(typeAndSubTypes);
296                         }
297                     }
298 
299                     EntitySetBase extent = extentMap.Set;
300                     Debug.Assert(extent != null, "Extent map for a null extent or type of extentMap.Exent " +
301                                  "is not Extent");
302 
303                     // For each table mapping for the type mapping, we create cells
304                     foreach (StorageMappingFragment fragmentMap in typeMap.MappingFragments)
305                     {
306                         ExtractCellsFromTableFragment(extent, fragmentMap, allTypes, cells);
307                     }
308                 }
309             }
310         }
311 
312         // effects: Given an extent's ("extent") table fragment that is
313         // contained inside typeMap, determine the cells that need to be
314         // created and add them to cells
315         // allTypes corresponds to all the different types that the type map
316         // represents -- this parameter has something useful only if extent
317         // is an entity set
ExtractCellsFromTableFragment(EntitySetBase extent, StorageMappingFragment fragmentMap, Set<EdmType> allTypes, List<Cell> cells)318         private void ExtractCellsFromTableFragment(EntitySetBase extent, StorageMappingFragment fragmentMap,
319                                                    Set<EdmType> allTypes, List<Cell> cells)
320         {
321 
322             // create C-query components
323             MemberPath cRootExtent = new MemberPath(extent);
324             BoolExpression cQueryWhereClause = BoolExpression.True;
325             List<ProjectedSlot> cSlots = new List<ProjectedSlot>();
326 
327             if (allTypes.Count > 0)
328             {
329                 // Create a type condition for the extent, i.e., "extent in allTypes"
330                 cQueryWhereClause = BoolExpression.CreateLiteral(new TypeRestriction(cRootExtent, allTypes), null);
331             }
332 
333             // create S-query components
334             MemberPath sRootExtent = new MemberPath(fragmentMap.TableSet);
335             BoolExpression sQueryWhereClause = BoolExpression.True;
336             List<ProjectedSlot> sSlots = new List<ProjectedSlot>();
337 
338             // Association or entity set
339             // Add the properties and the key properties to a list and
340             // then process them in ExtractProperties
341             ExtractProperties(fragmentMap.AllProperties, cRootExtent, cSlots, ref cQueryWhereClause, sRootExtent, sSlots, ref sQueryWhereClause);
342 
343             // limitation of MSL API: cannot assign constant values to table columns
344             CellQuery cQuery = new CellQuery(cSlots, cQueryWhereClause, cRootExtent, CellQuery.SelectDistinct.No /*no distinct flag*/);
345             CellQuery sQuery = new CellQuery(sSlots, sQueryWhereClause, sRootExtent,
346                                         fragmentMap.IsSQueryDistinct ? CellQuery.SelectDistinct.Yes : CellQuery.SelectDistinct.No);
347 
348             StorageMappingFragment fragmentInfo = fragmentMap as StorageMappingFragment;
349             Debug.Assert((fragmentInfo != null), "CSMappingFragment should support Line Info");
350             CellLabel label = new CellLabel(fragmentInfo);
351             Cell cell = Cell.CreateCS(cQuery, sQuery, label, m_currentCellNumber);
352             m_currentCellNumber++;
353             cells.Add(cell);
354         }
355 
356         // requires: "properties" corresponds to all the properties that are
357         // inside cNode.Value, e.g., cNode corresponds to an extent Person,
358         // properties contains all the properties inside Person (recursively)
359         // effects: Given C-side and S-side Cell Query for a cell, generates
360         // the projected slots on both sides corresponding to
361         // properties. Also updates the C-side whereclause corresponding to
362         // discriminator properties on the C-side, e.g, isHighPriority
ExtractProperties(IEnumerable<StoragePropertyMapping> properties, MemberPath cNode, List<ProjectedSlot> cSlots, ref BoolExpression cQueryWhereClause, MemberPath sRootExtent, List<ProjectedSlot> sSlots, ref BoolExpression sQueryWhereClause)363         private void ExtractProperties(IEnumerable<StoragePropertyMapping> properties,
364                                        MemberPath cNode, List<ProjectedSlot> cSlots,
365                                        ref BoolExpression cQueryWhereClause,
366                                        MemberPath sRootExtent,
367                                        List<ProjectedSlot> sSlots,
368                                        ref BoolExpression sQueryWhereClause)
369         {
370             // For each property mapping, we add an entry to the C and S cell queries
371             foreach (StoragePropertyMapping propMap in properties)
372             {
373                 StorageScalarPropertyMapping scalarPropMap = propMap as StorageScalarPropertyMapping;
374                 StorageComplexPropertyMapping complexPropMap = propMap as StorageComplexPropertyMapping;
375                 StorageEndPropertyMapping associationEndPropertypMap = propMap as StorageEndPropertyMapping;
376                 StorageConditionPropertyMapping conditionMap = propMap as StorageConditionPropertyMapping;
377 
378                 Debug.Assert(scalarPropMap != null ||
379                              complexPropMap != null ||
380                              associationEndPropertypMap != null ||
381                              conditionMap != null, "Unimplemented property mapping");
382 
383                 if (scalarPropMap != null)
384                 {
385                     Debug.Assert(scalarPropMap.ColumnProperty != null, "ColumnMember for a Scalar Property can not be null");
386                     // Add an attribute node to node
387 
388                     MemberPath cAttributeNode = new MemberPath(cNode, scalarPropMap.EdmProperty);
389                     // Add a column (attribute) node the sQuery
390                     // unlike the C side, there is no nesting. Hence we
391                     // did not need an internal node
392                     MemberPath sAttributeNode = new MemberPath(sRootExtent, scalarPropMap.ColumnProperty);
393                     cSlots.Add(new MemberProjectedSlot(cAttributeNode));
394                     sSlots.Add(new MemberProjectedSlot(sAttributeNode));
395                 }
396 
397                 // Note: S-side constants are not allowed since they can cause
398                 // problems -- for example, if such a cell says 5 for the
399                 // third field, we cannot guarantee the fact that an
400                 // application may not set that field to 7 in the C-space
401 
402                 // Check if the property mapping is for a complex types
403                 if (complexPropMap != null)
404                 {
405                     foreach (StorageComplexTypeMapping complexTypeMap in complexPropMap.TypeMappings)
406                     {
407                         // Create a node for the complex type property and call recursively
408                         MemberPath complexMemberNode = new MemberPath(cNode, complexPropMap.EdmProperty);
409                         //Get the list of types that this type map represents
410                         Set<EdmType> allTypes = new Set<EdmType>();
411                         // Gather a set of all explicit types for an entity
412                         // type mapping in allTypes.
413                         IEnumerable<EdmType> exactTypes = Helpers.AsSuperTypeList<ComplexType, EdmType>(complexTypeMap.Types);
414                         allTypes.AddRange(exactTypes);
415                         foreach (EdmType type in complexTypeMap.IsOfTypes)
416                         {
417                             allTypes.AddRange(MetadataHelper.GetTypeAndSubtypesOf(type, m_containerMapping.StorageMappingItemCollection.EdmItemCollection, false /*includeAbstractTypes*/));
418                         }
419                         BoolExpression complexInTypes = BoolExpression.CreateLiteral(new TypeRestriction(complexMemberNode, allTypes), null);
420                         cQueryWhereClause = BoolExpression.CreateAnd(cQueryWhereClause, complexInTypes);
421                         // Now extract the properties of the complex type
422                         // (which could have other complex types)
423                         ExtractProperties(complexTypeMap.AllProperties, complexMemberNode, cSlots,
424                                           ref cQueryWhereClause, sRootExtent, sSlots, ref sQueryWhereClause);
425                     }
426                 }
427 
428                 // Check if the property mapping is for an associaion
429                 if (associationEndPropertypMap != null)
430                 {
431                     // create join tree node representing this relation end
432                     MemberPath associationEndNode = new MemberPath(cNode, associationEndPropertypMap.EndMember);
433                     // call recursively
434                     ExtractProperties(associationEndPropertypMap.Properties, associationEndNode, cSlots,
435                                       ref cQueryWhereClause, sRootExtent, sSlots, ref sQueryWhereClause);
436                 }
437 
438                 //Check if the this is a condition and add it to the Where clause
439                 if (conditionMap != null)
440                 {
441                     if (conditionMap.ColumnProperty != null)
442                     {
443                         //Produce a Condition Expression for the Condition Map.
444                         BoolExpression conditionExpression = GetConditionExpression(sRootExtent, conditionMap);
445                         //Add the condition expression to the exisiting S side Where clause using an "And"
446                         sQueryWhereClause = BoolExpression.CreateAnd(sQueryWhereClause, conditionExpression);
447                     }
448                     else
449                     {
450                         Debug.Assert(conditionMap.EdmProperty != null);
451                         //Produce a Condition Expression for the Condition Map.
452                         BoolExpression conditionExpression = GetConditionExpression(cNode, conditionMap);
453                         //Add the condition expression to the exisiting C side Where clause using an "And"
454                         cQueryWhereClause = BoolExpression.CreateAnd(cQueryWhereClause, conditionExpression);
455                     }
456 
457                 }
458             }
459         }
460 
461         /// <summary>
462         /// Takes in a JoinTreeNode and a Contition Property Map and creates an BoolExpression
463         /// for the Condition Map.
464         /// </summary>
465         /// <param name="joinTreeNode"></param>
466         /// <param name="conditionMap"></param>
467         /// <returns></returns>
GetConditionExpression(MemberPath member, StorageConditionPropertyMapping conditionMap)468         private static BoolExpression GetConditionExpression(MemberPath member, StorageConditionPropertyMapping conditionMap)
469         {
470             //Get the member for which the condition is being specified
471             EdmMember conditionMember = (conditionMap.ColumnProperty != null) ? conditionMap.ColumnProperty : conditionMap.EdmProperty;
472 
473             MemberPath conditionMemberNode = new MemberPath(member, conditionMember);
474             //Check if this is a IsNull condition
475             MemberRestriction conditionExpression = null;
476             if (conditionMap.IsNull.HasValue)
477             {
478                 // for conditions on scalars, create NodeValue nodes, otherwise NodeType
479                 Constant conditionConstant = (true == conditionMap.IsNull.Value) ? Constant.Null : Constant.NotNull;
480                 if (true == MetadataHelper.IsNonRefSimpleMember(conditionMember))
481                 {
482                     conditionExpression = new ScalarRestriction(conditionMemberNode, conditionConstant);
483                 }
484                 else
485                 {
486                     conditionExpression = new TypeRestriction(conditionMemberNode, conditionConstant);
487                 }
488             }
489             else
490             {
491                 conditionExpression = new ScalarRestriction(conditionMemberNode, new ScalarConstant(conditionMap.Value));
492             }
493 
494             Debug.Assert(conditionExpression != null);
495 
496             return BoolExpression.CreateLiteral(conditionExpression, null);
497         }
498 
IsBooleanMember(MemberPath path)499         private static bool IsBooleanMember(MemberPath path)
500         {
501             PrimitiveType primitive = path.EdmType as PrimitiveType;
502             return (primitive != null && primitive.PrimitiveTypeKind == PrimitiveTypeKind.Boolean);
503         }
504         #endregion
505 
506         #region String methods
ToCompactString(System.Text.StringBuilder builder)507         internal override void ToCompactString(System.Text.StringBuilder builder)
508         {
509             builder.Append("CellCreator"); // No state to really show i.e., m_maps
510         }
511 
512         #endregion
513 
514     }
515 }
516