1 chuck 1.1.2.12 //%2003////////////////////////////////////////////////////////////////////////
|
2 chuck 1.1.2.3 //
3 // Copyright (c) 2000, 2001, 2002 BMC Software, Hewlett-Packard Development
4 // Company, L. P., IBM Corp., The Open Group, Tivoli Systems.
|
5 chuck 1.1.2.13 // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L. P.;
|
6 chuck 1.1.2.3 // IBM Corp.; EMC Corporation, The Open Group.
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to
10 // deal in the Software without restriction, including without limitation the
11 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 // sell copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
16 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
17 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
18 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
19 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //==============================================================================
25 //
26 // Authors: David Rosckes (rosckes@us.ibm.com)
27 chuck 1.1.2.3 // Bert Rivero (hurivero@us.ibm.com)
28 // Chuck Carmack (carmack@us.ibm.com)
29 // Brian Lucier (lucier@us.ibm.com)
30 //
31 // Modified By:
32 //
33 //%/////////////////////////////////////////////////////////////////////////////
34
|
35 humberto 1.1.2.1 #include "CQLSelectStatement.h"
36 #include "CQLSelectStatementRep.h"
37
|
38 chuck 1.1.2.14 #include <iostream>
39
|
40 chuck 1.1.2.5 #include <Pegasus/Common/CIMValue.h>
41 #include <Pegasus/Common/CIMInstance.h>
42 #include <Pegasus/Common/CIMProperty.h>
43 #include <Pegasus/Common/InternalException.h>
|
44 chuck 1.1.2.6 #include <Pegasus/Common/Exception.h>
45 #include <Pegasus/Common/CIMStatusCode.h>
|
46 chuck 1.1.2.14 #include <Pegasus/Common/AutoPtr.h>
|
47 chuck 1.1.2.5 #include "CQLValue.h"
48 #include "CQLIdentifier.h"
49 #include "CQLChainedIdentifier.h"
50
|
51 chuck 1.1.2.6 // ATTN: TODOs -
52 // spec compliance
53 // assertions
54 // optimize
55 // localized CQL exceptions
56 // documentation
57 // trace? but this could be used by provider
|
58 chuck 1.1.2.5
|
59 humberto 1.1.2.1 PEGASUS_NAMESPACE_BEGIN
60
|
61 chuck 1.1.2.6 struct PropertyNode
|
62 chuck 1.1.2.5 {
|
63 chuck 1.1.2.20 CIMName name; // property name
64 CIMName scope; // class the property is on
65 Boolean wildcard; // true if this property is a wildcard
|
66 chuck 1.1.2.14 AutoPtr<PropertyNode> sibling;
67 AutoPtr<PropertyNode> firstChild;
68
69 PropertyNode() {/*PEGASUS_STD(cout) << "new " << this << PEGASUS_STD(endl);*/}
70 ~PropertyNode() {/*PEGASUS_STD(cout) << "delete " << this << PEGASUS_STD(endl);*/}
|
71 chuck 1.1.2.5 };
72
73
|
74 chuck 1.1.2.3 CQLSelectStatementRep::CQLSelectStatementRep()
|
75 chuck 1.1.2.9 :SelectStatementRep(),
|
76 chuck 1.1.2.16 _hasWhereClause(false),
77 _contextApplied(false)
|
78 humberto 1.1.2.1 {
79 }
80
|
81 chuck 1.1.2.16 CQLSelectStatementRep::CQLSelectStatementRep(String& inQlang,
|
82 chuck 1.1.2.17 String& inQuery,
83 QueryContext& inCtx)
|
84 chuck 1.1.2.9 :SelectStatementRep(inQlang, inQuery, inCtx),
|
85 chuck 1.1.2.16 _hasWhereClause(false),
86 _contextApplied(false)
|
87 chuck 1.1.2.3 {
|
88 humberto 1.1.2.2 }
89
|
90 chuck 1.1.2.3 CQLSelectStatementRep::CQLSelectStatementRep(const CQLSelectStatementRep& rep)
91 :SelectStatementRep(rep),
92 _selectIdentifiers(rep._selectIdentifiers),
|
93 chuck 1.1.2.9 _hasWhereClause(rep._hasWhereClause),
|
94 chuck 1.1.2.16 _predicate(rep._predicate),
95 _contextApplied(rep._contextApplied)
|
96 humberto 1.1.2.1 {
97 }
98
|
99 chuck 1.1.2.3 CQLSelectStatementRep::~CQLSelectStatementRep()
|
100 humberto 1.1.2.1 {
101 }
102
|
103 chuck 1.1.2.3 CQLSelectStatementRep& CQLSelectStatementRep::operator=(const CQLSelectStatementRep& rhs)
|
104 humberto 1.1.2.1 {
|
105 chuck 1.1.2.3 if (this == &rhs)
106 return *this;
107
108 SelectStatementRep::operator=(rhs);
109
110 _selectIdentifiers = rhs._selectIdentifiers;
111 _predicate = rhs._predicate;
|
112 chuck 1.1.2.16 _contextApplied = rhs._contextApplied;
113 _hasWhereClause = rhs._hasWhereClause;
|
114 chuck 1.1.2.3
115 return *this;
116 }
117
118 Boolean CQLSelectStatementRep::evaluate(const CIMInstance& inCI)
119 {
|
120 chuck 1.1.2.4 if (!hasWhereClause())
|
121 chuck 1.1.2.16 {
|
122 chuck 1.1.2.4 return true;
|
123 chuck 1.1.2.16 }
|
124 chuck 1.1.2.4 else
|
125 chuck 1.1.2.11 {
|
126 chuck 1.1.2.16 if (!_contextApplied)
127 applyContext();
128
|
129 chuck 1.1.2.11 try
130 {
131 return _predicate.evaluate(inCI, *_ctx);
132 }
133 catch (UninitializedObjectException& )
134 {
135 // The null contagion rule.
136 // ATTN change this to a specific CQLException
137 return false;
138 }
139 }
|
140 chuck 1.1.2.3 }
141
142 void CQLSelectStatementRep::applyProjection(CIMInstance& inCI) throw(Exception)
143 {
|
144 chuck 1.1.2.16 if (!_contextApplied)
145 applyContext();
|
146 chuck 1.1.2.3
|
147 chuck 1.1.2.20 //
148 // Build a tree to represent the projected properties from the select list
149 // of chained identifiers. This is needed because embedded instances below
150 // the FROM class form a tree structure when projected.
151 //
152 // The design of the tree is to gather all the required properties for
153 // an instance at a node as child nodes. The root node
154 // of the tree represents the instance passed in to this function. Below the
155 // root there can be nodes that are required embedded instance properties.
156 // The child nodes of these embedded instance nodes represent the required
157 // properties on the embedded instance (which may themselves be embedded instances).
158 //
159 // Each node has a name, which is in 2 parts -- the property name and the
160 // scope (ie. the class the property is on). This allows the scoping
161 // operator to be handled correctly, so that the parent instance can be
162 // checked to see if it is the right class to provide the property.
163 // Note that the scoping is a base class; ie. the parent instance of a node
164 // may be a subclass of the scope.
165 //
166
167 // Set up the root node of the tree. This represents the instance
168 chuck 1.1.2.20 // passed in.
|
169 chuck 1.1.2.14 AutoPtr<PropertyNode> rootNode(new PropertyNode);
|
170 chuck 1.1.2.5 Array<CQLIdentifier> fromList = _ctx->getFromList();
171 rootNode->name = fromList[0].getName(); // not doing joins
|
172 chuck 1.1.2.20 rootNode->scope = fromList[0].getName(); // not used on root, just to fill in the var
|
173 chuck 1.1.2.17 rootNode->wildcard = false;
|
174 chuck 1.1.2.20
175 // Build the tree below the root.
|
176 chuck 1.1.2.5 for (Uint32 i = 0; i < _selectIdentifiers.size(); i++)
177 {
|
178 chuck 1.1.2.20 // Get the chain elements
|
179 chuck 1.1.2.16 Array<CQLIdentifier> ids = _selectIdentifiers[i].getSubIdentifiers();
|
180 chuck 1.1.2.6
|
181 chuck 1.1.2.20 PEGASUS_ASSERT(ids.size() > 1);
|
182 chuck 1.1.2.5
|
183 chuck 1.1.2.14 PropertyNode * curNode = rootNode.get();
184 PropertyNode * curChild = curNode->firstChild.get();
|
185 chuck 1.1.2.5
|
186 chuck 1.1.2.20 // Loop through the identifiers in the chain.
187 // NOTE: this starts at the position *after* the FROM class
188 // So, the loop index is always one position after the current node,
189 // ie. it will become a child node of the current node.
|
190 chuck 1.1.2.5 for (Uint32 j = 1; j < ids.size(); j++)
191 {
|
192 chuck 1.1.2.20 // If the child is wildcarded, then every property exposed by the
193 // class of the instance at the current node is required.
194 // Mark the current node as wildcarded.
|
195 chuck 1.1.2.5 if (ids[j].isWildcard())
196 {
|
197 chuck 1.1.2.17 curNode->wildcard = true;
198 break;
|
199 chuck 1.1.2.5 }
200
|
201 chuck 1.1.2.20 // Determine if this identifier is already a child node of
202 // the current node.
|
203 chuck 1.1.2.5 Boolean found = false;
204 while (curChild != NULL && !found)
205 {
|
206 chuck 1.1.2.20 // The scoping class is either the scope of the identifier
207 // or the FROM class if the identifier is not scoped.
208 String scope = fromList[0].getName().getString();
209 if (ids[j].isScoped())
210 {
211 scope = ids[j].getScope();
212 }
213
|
214 chuck 1.1.2.17 if (curChild->name == ids[j].getName() &&
|
215 chuck 1.1.2.20 String::equalNoCase(curChild->scope.getString(), scope))
|
216 chuck 1.1.2.5 {
|
217 chuck 1.1.2.20 // Name and scope match. The identifier already has a child node.
|
218 chuck 1.1.2.17 found = true;
219 }
220 else
|
221 chuck 1.1.2.5 {
|
222 chuck 1.1.2.17 curChild = curChild->sibling.get();
223 }
|
224 chuck 1.1.2.5 }
225
226 if (!found)
227 {
|
228 chuck 1.1.2.20 // The identifier doesn't already have a child node.
229 // Create a node and add it as a child to the current node.
|
230 chuck 1.1.2.17 curChild = new PropertyNode;
231 curChild->sibling = curNode->firstChild;
232 curChild->name = ids[j].getName();
233 curChild->wildcard = false;
234 curNode->firstChild.reset(curChild); // safer than using the = operator
235 }
|
236 chuck 1.1.2.16
|
237 chuck 1.1.2.20 // Set the scope for the child node
|
238 chuck 1.1.2.17 if (ids[j].isScoped())
239 {
|
240 chuck 1.1.2.20 // Child node has a scoping class
241 PEGASUS_ASSERT(ids[j].getScope().size() > 0);
242 curChild->scope = CIMName(ids[j].getScope());
|
243 chuck 1.1.2.17 }
244 else
245 {
|
246 chuck 1.1.2.20 // Not scoped. The scope is the FROM class.
247 PEGASUS_ASSERT(j == 1);
248 PEGASUS_ASSERT(fromList[0].getName().getString().size() > 0);
249 curChild->scope = fromList[0].getName();
|
250 chuck 1.1.2.5 }
251
252 curNode = curChild;
|
253 chuck 1.1.2.14 curChild = curNode->firstChild.get();
|
254 chuck 1.1.2.5 }
255 }
256
|
257 chuck 1.1.2.20 //
258 // Do the projection.
259 //
260
|
261 chuck 1.1.2.5 Array<CIMName> requiredProps;
|
262 chuck 1.1.2.17 Boolean allPropsRequired = rootNode->wildcard;
263
|
264 chuck 1.1.2.20 // Loop through the children of the root node.
265 // The root node represents the FROM class,
266 // and the child nodes are the required properties on the FROM class.
|
267 chuck 1.1.2.15 PropertyNode* childNode = rootNode->firstChild.get();
268 while (childNode != NULL)
|
269 chuck 1.1.2.5 {
|
270 chuck 1.1.2.20 // Determine if the instance passed in meets the class scoping
271 // rules for the current child node.
272 Boolean filterable = isFilterable(inCI, childNode);
273
274 // If the instance is filterable, and the child node has children,
275 // then the child is assumed to be an embedded instance, and we
276 // need to recurse to apply the projection on the embedded instance.
277 // (the check for embedded instance is done in the recursive call)
278 if (filterable && childNode->firstChild.get() != NULL)
279 {
280 // We need to project on an embedded instance property. The steps are to
281 // remove the embedded instance property from the instance passed in,
282 // project on that embedded instance property, and then add the projected
283 // embedded instance property back to the instance passed in.
|
284 chuck 1.1.2.15 Uint32 index = inCI.findProperty(childNode->name);
|
285 chuck 1.1.2.20 if (index != PEG_NOT_FOUND)
286 {
287 // The embedded instance has the required embedded instance property.
288 CIMProperty childProp = inCI.getProperty(index);
289 inCI.removeProperty(index);
290 applyProjection(childNode, childProp);
291 inCI.addProperty(childProp);
292 }
|
293 chuck 1.1.2.5 }
294
|
295 chuck 1.1.2.20 // If the node is not wildcarded, and the instance passed in
296 // is filterable, then add the current child to the list
297 // of required properties on the instance passed in.
298 if (!allPropsRequired && filterable)
|
299 chuck 1.1.2.16 {
|
300 chuck 1.1.2.20 requiredProps.append(childNode->name);
|
301 chuck 1.1.2.16 }
302
|
303 chuck 1.1.2.17 childNode = childNode->sibling.get();
|
304 chuck 1.1.2.16 }
305
|
306 chuck 1.1.2.20 // Remove the properties that are not in the projection.
307 // This also checks for missing required properties.
|
308 chuck 1.1.2.21 removeUnneededProperties(inCI,
309 allPropsRequired,
310 fromList[0].getName(),
311 requiredProps);
|
312 chuck 1.1.2.5 }
313
|
314 chuck 1.1.2.6 void CQLSelectStatementRep::applyProjection(PropertyNode* node,
|
315 chuck 1.1.2.17 CIMProperty& nodeProp)
|
316 chuck 1.1.2.5 {
|
317 chuck 1.1.2.14 PEGASUS_ASSERT(node->firstChild.get() != NULL);
|
318 chuck 1.1.2.5
|
319 chuck 1.1.2.20 //
320 // The property passed in must be an embedded instance. It is not
321 // allowed to project properties on embedded classes.
322 //
323 // Get the embedded instance from the property.
324 //
325
|
326 chuck 1.1.2.5 CIMValue nodeVal = nodeProp.getValue();
|
327 chuck 1.1.2.20 // ATTN - UNCOMMENT when emb objs are supported
328 /*
329 if (nodeVal.getType() != CIMTYPE_OBJECT)
330 {
331 throw Exception("TEMP MSG: applyProjection - property must be emb obj");
332 }
333 */
334
335 CIMObject nodeObj;
336 // ATTN - UNCOMMENT when emb objs are supported
337 // nodeVal.get(nodeObj);
338 if (!nodeObj.isInstance())
339 {
340 throw Exception("TEMP MSG: applyProjection - not allowed to project properties on classes");
341 }
342
343 CIMInstance nodeInst(nodeObj);
344
345 //
346 // Do the projection.
347 //
|
348 chuck 1.1.2.5
349 Array<CIMName> requiredProps;
|
350 chuck 1.1.2.17 Boolean allPropsRequired = node->wildcard;
|
351 chuck 1.1.2.5
|
352 chuck 1.1.2.20 // Loop through the children of the node.
353 // The node represents an embedded instance,
354 // and the child nodes are the required properties on the embedded instance.
|
355 chuck 1.1.2.14 PropertyNode * curChild = node->firstChild.get();
|
356 chuck 1.1.2.5 while (curChild != NULL)
357 {
|
358 chuck 1.1.2.20 // Determine if the embedded instance meets the class scoping
359 // rules for the current child node
360 Boolean filterable = isFilterable(nodeInst, curChild);
361
362 // If the embedded instance is filterable, and the child node has children,
363 // then the child is assumed to be an embedded instance, and we
364 // need to recurse to apply the projection on the child embedded instance.
365 // (the check for embedded instance is done in the recursive call)
366 if (filterable && curChild->firstChild.get() != NULL)
367 {
368 // We need to project on an embedded instance property. The steps are to
369 // remove the embedded instance property from the current instance,
370 // project on that embedded instance property, and then add the projected
371 // embedded instance property back to the current instance.
|
372 chuck 1.1.2.5 Uint32 index = nodeInst.findProperty(curChild->name);
|
373 chuck 1.1.2.20 if (index != PEG_NOT_FOUND)
374 {
375 // The embedded instance has the required embedded instance property.
376 CIMProperty childProp = nodeInst.getProperty(index);
377 nodeInst.removeProperty(index);
378 applyProjection(curChild, childProp);
379 nodeInst.addProperty(childProp);
380 }
|
381 chuck 1.1.2.5 }
382
|
383 chuck 1.1.2.20 // If the node is not wildcarded, and the embedded instance
384 // is filterable, then add the current child to the list
385 // of required properties on the embedded instance.
386 if (!allPropsRequired && filterable)
|
387 chuck 1.1.2.14 {
|
388 chuck 1.1.2.20 // The instance is filterable, add the property to the required list.
389 requiredProps.append(node->name);
|
390 chuck 1.1.2.16 }
391
|
392 chuck 1.1.2.14 curChild = curChild->sibling.get();
|
393 chuck 1.1.2.5 }
394
|
395 chuck 1.1.2.20 // Remove the properties that are not in the projection.
396 // This also checks for missing required properties.
|
397 chuck 1.1.2.21 removeUnneededProperties(nodeInst,
398 allPropsRequired,
399 nodeInst.getClassName(),
400 requiredProps);
|
401 chuck 1.1.2.5
|
402 chuck 1.1.2.20 // Put the projected instance back into the property.
|
403 chuck 1.1.2.5 // ATTN - UNCOMMENT when emb objs are supported
404 //CIMValue newNodeVal(nodeInst);
405 //nodeProp.setValue(newNodeVal);
406 }
407
|
408 chuck 1.1.2.20 Boolean CQLSelectStatementRep::isFilterable(const CIMInstance& inst,
409 PropertyNode* node)
410 {
411 //
412 // Determine if an instance is filterable for a scoped property (ie. its
413 // type is the scoping class or a subclass of the scoping class where the
414 // property exists)
415 //
416 // Note that an instance that is unfilterable is not considered
417 // an error. In CQL, an instance that is not of the required scope
418 // would cause a NULL to be placed in the result set column for the
419 // property. However since we are not implementing result set in stage1,
420 // just skip the property. This can lead to an instance having
421 // NO required properties even though it is derived from the FROM class.
422 // This can easily happen if the scoping operator is used.
423 //
|
424 chuck 1.1.2.17
425 Boolean filterable = false;
426 if (inst.getClassName() == node->scope)
427 {
|
428 chuck 1.1.2.20 // The instance's class is the same as the required scope
|
429 chuck 1.1.2.17 filterable = true;
430 }
|
431 chuck 1.1.2.20 else
|
432 chuck 1.1.2.17 {
|
433 chuck 1.1.2.20 try
434 {
435 if (_ctx->isSubClass(node->scope, inst.getClassName()))
436 {
437 // The instance's class is a subclass or of the required scope.
438 filterable = true;
439 }
440 }
441 catch (CIMException& ce)
442 {
443 if (ce.getCode() == CIM_ERR_INVALID_CLASS ||
444 ce.getCode() == CIM_ERR_NOT_FOUND)
445 {
446 // The scoping class was not found in the schema.
447 // Just swallow this error because according to the
448 // spec we should be putting NULL in the result column,
449 // which means skipping the property on the instance.
450 ;
451 }
452 }
|
453 chuck 1.1.2.17 }
|
454 chuck 1.1.2.20
455 return filterable;
|
456 chuck 1.1.2.17 }
457
|
458 chuck 1.1.2.5 void CQLSelectStatementRep::removeUnneededProperties(CIMInstance& inst,
|
459 chuck 1.1.2.21 Boolean& allPropsRequired,
460 const CIMName& allPropsClass,
|
461 chuck 1.1.2.17 Array<CIMName>& requiredProps)
|
462 chuck 1.1.2.5 {
|
463 chuck 1.1.2.20 // Implementation note:
464 // Scoping operator before a wildcard is not allowed:
465 // Example:
466 // SELECT fromclass.embobj1.scope1::* FROM fromclass
467 //
|
468 chuck 1.1.2.21 // However, the following are allowed:
469 // SELECT fromclass.embobj1.* FROM fromclass
470 // (this means that all the properties on the class of instance embobj1
471 // are required)
472 //
473 // SELECT fromclass.* FROM fromclass
474 // (this that all the properties on class fromclass are required
475 // to be on the instance being projected, not including any
476 // properties on a subclass of fromclass)
|
477 chuck 1.1.2.20
478 // If all properties are required (ie. wildcarded), then rebuild the
479 // required property list from all the properties on the FROM class
|
480 chuck 1.1.2.17 if (allPropsRequired)
|
481 chuck 1.1.2.16 {
|
482 chuck 1.1.2.17 requiredProps.clear();
|
483 chuck 1.1.2.21 CIMClass cls = _ctx->getClass(allPropsClass);
|
484 chuck 1.1.2.16 Array<CIMName> clsProps;
485 for (Uint32 i = 0; i < cls.getPropertyCount(); i++)
486 {
487 requiredProps.append(cls.getProperty(i).getName());
488 }
489 }
490
|
491 chuck 1.1.2.20 // Find out what properties are on the instance.
|
492 chuck 1.1.2.16 Array<CIMName> supportedProps;
|
493 chuck 1.1.2.5 for (Uint32 i = 0; i < inst.getPropertyCount(); i++)
494 {
|
495 chuck 1.1.2.16 supportedProps.append(inst.getProperty(i).getName());
496 }
497
|
498 chuck 1.1.2.20 // Check that all required properties are on the instance.
|
499 chuck 1.1.2.16 for (Uint32 i = 0; i < requiredProps.size(); i++)
500 {
501 if (!containsProperty(requiredProps[i], supportedProps))
|
502 chuck 1.1.2.5 {
|
503 chuck 1.1.2.17 throw Exception("TEMP MSG - instance missing required property: " + requiredProps[i].getString());
|
504 chuck 1.1.2.5 }
|
505 chuck 1.1.2.16 }
|
506 chuck 1.1.2.5
|
507 chuck 1.1.2.20 // Remove the properties on the instance that are not required.
|
508 chuck 1.1.2.16 for (Uint32 i = 0; i < supportedProps.size(); i++)
509 {
510 if (!containsProperty(supportedProps[i], requiredProps))
|
511 chuck 1.1.2.5 {
|
512 chuck 1.1.2.17 Uint32 index = inst.findProperty(supportedProps[i]);
513 PEGASUS_ASSERT(index != PEG_NOT_FOUND);
514 inst.removeProperty(index);
|
515 chuck 1.1.2.5 }
516 }
|
517 humberto 1.1.2.1 }
518
519 void CQLSelectStatementRep::validateClass(const CIMObjectPath& inClassName) throw(Exception)
520 {
|
521 chuck 1.1.2.5 Array<CQLIdentifier> fromList = _ctx->getFromList();
522 PEGASUS_ASSERT(fromList.size() == 1); // no joins yet
523
|
524 chuck 1.1.2.22 // Check if the classname passed in is the FROM class or a subclass
525 // of the FROM class.
|
526 chuck 1.1.2.5 if (!inClassName.getClassName().equal(fromList[0].getName()))
527 {
|
528 chuck 1.1.2.22 // Not the FROM class, check if it is a subclass of the FROM
529 QueryContext::ClassRelation rel;
530 try
531 {
532 rel = _ctx->getClassRelation(fromList[0].getName(),
533 inClassName.getClassName());
534 }
535 catch (CIMException& ce)
536 {
537 if (ce.getCode() == CIM_ERR_INVALID_CLASS ||
538 ce.getCode() == CIM_ERR_NOT_FOUND)
539 {
540 // Either the FROM or the class passed in does not exist
541 // ATTN may just want to throw the CIMException rather than
542 // CQL exception
543 throw Exception("TEMP MSG - class does not exist: " + ce.getMessage());
544 }
|
545 chuck 1.1.2.5
|
546 chuck 1.1.2.22 throw;
547 }
548
549 if (rel != QueryContext::SUBCLASS)
550 {
551 throw Exception("TEMP MSG: class is not the FROM class and not a subclass of FROM class");
552 }
|
553 chuck 1.1.2.5 }
|
554 chuck 1.1.2.22 else
|
555 chuck 1.1.2.5 {
|
556 chuck 1.1.2.22 // Class passed in is the FROM class. Check if it exists.
557 try
|
558 chuck 1.1.2.5 {
|
559 chuck 1.1.2.22 _ctx->getClass(inClassName.getClassName());
560 }
561 catch (CIMException& ce)
562 {
563 if (ce.getCode() == CIM_ERR_INVALID_CLASS ||
564 ce.getCode() == CIM_ERR_NOT_FOUND)
565 {
566 // ATTN may just want to throw the CIMException rather than
567 // CQL exception
568 throw Exception("TEMP MSG - class does not exist " + ce.getMessage());
569 }
570
571 throw;
|
572 chuck 1.1.2.5 }
573 }
|
574 humberto 1.1.2.1 }
575
|
576 chuck 1.1.2.6 //
577 // Validates that all the chained identifiers in the statement meet
578 // the rules in the CQL spec vs.the class definitions in the repository
579 //
|
580 humberto 1.1.2.1 void CQLSelectStatementRep::validateProperties() throw(Exception)
581 {
|
582 chuck 1.1.2.16 if (!_contextApplied)
583 applyContext();
|
584 chuck 1.1.2.5
585 for (Uint32 i = 0; i < _selectIdentifiers.size(); i++)
586 {
587 validateProperty(_selectIdentifiers[i]);
588 }
|
589 chuck 1.1.2.23
590 Array<CQLChainedIdentifier> _whereIdentifiers = _ctx->getWhereList();
|
591 chuck 1.1.2.5 for (Uint32 i = 0; i < _whereIdentifiers.size(); i++)
592 {
593 validateProperty(_whereIdentifiers[i]);
594 }
595 }
596
|
597 chuck 1.1.2.6 //
598 // Validates that the chained identifier meets all the rules in the CQL
599 // spec vs.the class definitions in the repository
600 //
|
601 chuck 1.1.2.5 void CQLSelectStatementRep::validateProperty(CQLChainedIdentifier& chainId)
602 {
|
603 chuck 1.1.2.17 // Note: applyContext has been called beforehand
604
|
605 chuck 1.1.2.5 Array<CQLIdentifier> ids = chainId.getSubIdentifiers();
606
|
607 chuck 1.1.2.20 Uint32 startingPos = 0;
|
608 chuck 1.1.2.16 CIMName curContext;
|
609 chuck 1.1.2.5 for (Uint32 pos = startingPos; pos < ids.size(); pos++)
610 {
|
611 chuck 1.1.2.6 // Determine the current class context
|
612 chuck 1.1.2.5 if (ids[pos].isScoped())
613 {
|
614 chuck 1.1.2.20 // The chain element is scoped. Use the scoping
615 // class as the current context. Note: this depends
616 // on applyContext resolving the class aliases before we get here.
617 curContext = CIMName(ids[pos].getScope());
|
618 chuck 1.1.2.5 }
619 else
620 {
|
621 chuck 1.1.2.20 // The chain element is not scoped. Assume that we are
622 // before a position that is required to be scoped.
623 // (applyContext validates that the chained identifier
624 // has scoped identifiers in the required positions).
625 // The current context is the name at the first position,
626 // which must be a classname (ie. right side of ISA where
627 // the only element in the chain is a classname, or
628 // cases where the FROM class is the first, and maybe only,
629 // element of the chain).
630 PEGASUS_ASSERT((pos < startingPos + 2) || ids[pos].isWildcard());
631 curContext = ids[0].getName();
|
632 chuck 1.1.2.6 }
633
634 // Get the class definition of the current class context
635 CIMClass classDef;
636 try
637 {
638 classDef = _ctx->getClass(curContext);
639 }
640 catch (CIMException& ce)
641 {
642 if (ce.getCode() == CIM_ERR_NOT_FOUND ||
|
643 chuck 1.1.2.17 ce.getCode() == CIM_ERR_INVALID_CLASS)
|
644 chuck 1.1.2.6 {
|
645 chuck 1.1.2.17 // ATTN: better to just throw the CIMException rather
646 // than the CQL exception?
647 throw Exception("TEMP_MSG: class context does not exist: " + curContext.getString());
|
648 chuck 1.1.2.6 }
|
649 chuck 1.1.2.5 }
650
|
651 chuck 1.1.2.20 // Now do the checks for properties existing on the current class context
652 // and the class relationship rules in section 5.4.1.
653 // Only do these checks if the chain id has a property.
654 if (pos > startingPos)
|
655 chuck 1.1.2.5 {
|
656 chuck 1.1.2.17 if (ids[pos].isWildcard())
657 {
|
658 chuck 1.1.2.20 // The wildcard is at the end (verified by applyContext), so
659 // no checking is required at this position.
|
660 chuck 1.1.2.17 continue;
661 }
|
662 chuck 1.1.2.5
|
663 chuck 1.1.2.17 // Determine if the property name at the current position
664 // exists on the current class context.
665 Uint32 propertyIndex = classDef.findProperty(ids[pos].getName());
666 if (propertyIndex == PEG_NOT_FOUND)
667 {
|
668 chuck 1.1.2.20 throw Exception("TEMP MSG: prop not on class context: " + ids[pos].getName().getString());
|
669 chuck 1.1.2.17 }
670
671 // Checking class relationship rules in section 5.4.1.
672 // For validateProperties, this only applies to the first
|
673 chuck 1.1.2.20 // property in the chain. This is because once we get into
674 // embedded properties we don't know what the class will be
675 // until we have an instance.
676 if ((pos == (startingPos+1)) && !curContext.equal(ids[0].getName()))
677 {
678 // Its the first property, and the class context is not the FROM class.
679 // Check the class relationship between the scoping class and the FROM class.
680 if (_ctx->getClassRelation(ids[0].getName(), curContext) == QueryContext::NOTRELATED)
|
681 chuck 1.1.2.17 {
682 throw Exception("TEMP MSG: section 5.4.1 violation!");
683 }
684 }
685
686 // If the current position implies an embedded object, then
687 // verify that the property is an embedded object
|
688 chuck 1.1.2.20 if ((pos > startingPos) && (pos < (ids.size() - 1)))
|
689 chuck 1.1.2.5 {
|
690 chuck 1.1.2.17 CIMProperty embObj = classDef.getProperty(propertyIndex);
691 CIMName qual("EmbeddedObject");
692 if (embObj.findQualifier(qual) == PEG_NOT_FOUND)
693 {
694 throw Exception("TEMP MSG: property is not an embedded object: " + embObj.getName().getString());
695 }
|
696 chuck 1.1.2.5 }
697 }
698 }
699 }
700
701 CIMName CQLSelectStatementRep::lookupFromClass(const String& lookup)
702 {
703 CQLIdentifier id = _ctx->findClass(lookup);
704
705 return id.getName();
|
706 humberto 1.1.2.1 }
707
|
708 chuck 1.1.2.3 Array<CIMObjectPath> CQLSelectStatementRep::getClassPathList()
|
709 humberto 1.1.2.1 {
|
710 chuck 1.1.2.5 Array<CQLIdentifier> ids = _ctx->getFromList();
711 PEGASUS_ASSERT(ids.size() == 1); // no joins yet
712
713 // No wbem-uri support yet.
714 CIMObjectPath path(String::EMPTY, _ctx->getNamespace(), ids[0].getName());
715
716 Array<CIMObjectPath> paths;
717 paths.append(path);
718
719 return paths;
|
720 humberto 1.1.2.1 }
721
722 CIMPropertyList CQLSelectStatementRep::getPropertyList(const CIMObjectPath& inClassName)
723 {
|
724 chuck 1.1.2.16 if (!_contextApplied)
725 applyContext();
|
726 chuck 1.1.2.5
|
727 chuck 1.1.2.20 // Get the classname. Note: since wbem-uri is not supported yet,
728 // only use the classname part of the path.
|
729 chuck 1.1.2.5 CIMName className = inClassName.getClassName();
|
730 chuck 1.1.2.20 if (className.isNull())
731 {
732 // If the caller passed in an empty className, then the
733 // FROM class is to be used.
734 className = _ctx->getFromList()[0].getName();
735 }
736
|
737 chuck 1.1.2.5 Boolean isWildcard;
738 Array<CIMName> reqProps;
|
739 chuck 1.1.2.20 Array<CIMName> matchedScopes;
740 Array<CIMName> unmatchedScopes;
741
742 // Add required properties from the select list.
|
743 chuck 1.1.2.5 for (Uint32 i = 0; i < _selectIdentifiers.size(); i++)
744 {
|
745 chuck 1.1.2.20 isWildcard = addRequiredProperty(reqProps,
746 className,
747 _selectIdentifiers[i],
748 matchedScopes,
749 unmatchedScopes);
|
750 chuck 1.1.2.5
751 if (isWildcard)
752 {
|
753 chuck 1.1.2.20 // If any wildcard is found then all properties are required.
754 // Return null property list to indicate all properties required.
|
755 chuck 1.1.2.5 return CIMPropertyList();
756 }
757 }
758
|
759 chuck 1.1.2.20 // Add required properties from the WHERE clause.
|
760 chuck 1.1.2.23 Array<CQLChainedIdentifier> _whereIdentifiers = _ctx->getWhereList();
|
761 chuck 1.1.2.5 for (Uint32 i = 0; i < _whereIdentifiers.size(); i++)
762 {
|
763 chuck 1.1.2.20 isWildcard = addRequiredProperty(reqProps,
764 className,
765 _whereIdentifiers[i],
766 matchedScopes,
767 unmatchedScopes);
|
768 chuck 1.1.2.5
|
769 chuck 1.1.2.20 // Wildcards are not allowed in the WHERE clause
|
770 chuck 1.1.2.5 PEGASUS_ASSERT(!isWildcard);
771 }
772
|
773 chuck 1.1.2.20 // Check if every property on the class is required.
774 CIMClass theClass = _ctx->getClass(className);
|
775 chuck 1.1.2.5 Uint32 propCnt = theClass.getPropertyCount();
776 Boolean allProps = true;
777 for (Uint32 i = 0; i < propCnt; i++)
778 {
779 if (!containsProperty(theClass.getProperty(i).getName(), reqProps))
780 {
781 allProps = false;
782 break;
783 }
784 }
785
786 if (allProps)
787 {
|
788 chuck 1.1.2.20 // Return null property list to indicate all properties are required.
|
789 chuck 1.1.2.5 return CIMPropertyList();
790 }
791 else
792 {
|
793 chuck 1.1.2.20 // Return the required property list. Note that it is possible to return
794 // an empty list in the case of no required properties for the classname
795 // passed in. This can happen when the scoping operator is used.
|
796 chuck 1.1.2.5 return CIMPropertyList(reqProps);
797 }
798 }
799
800 Boolean CQLSelectStatementRep::addRequiredProperty(Array<CIMName>& reqProps,
|
801 chuck 1.1.2.20 CIMName& className,
802 CQLChainedIdentifier& chainId,
803 Array<CIMName>& matchedScopes,
804 Array<CIMName>& unmatchedScopes)
805 {
806 //
807 // Implementation notes:
808 // This function does not look for required properties on embedded objects.
809 // This function assumes that applyContext has been called.
810 //
|
811 chuck 1.1.2.5
812 Array<CQLIdentifier> ids = chainId.getSubIdentifiers();
813
|
814 chuck 1.1.2.20 // After applyContext has been called, a single element
815 // chained identifier refers to either an instance of the
816 // FROM class, or is the classname on the right side of ISA.
|
817 chuck 1.1.2.17 if (ids.size() == 1)
818 {
|
819 chuck 1.1.2.20 // This identifier is not a property name
|
820 chuck 1.1.2.17 return false;
821 }
|
822 chuck 1.1.2.5
|
823 chuck 1.1.2.20 if (ids[1].isSymbolicConstant())
|
824 chuck 1.1.2.5 {
|
825 chuck 1.1.2.20 // Non-embedded symbolic constants are not properties
826 // Note that an embedded symbolic constant like this:
827 // fromclass.embobj.scope::someprop#'ok'
828 // implies that embobj is a required property, because
829 // embobj could be null, and that affects how the
830 // identifier is evaluated.
|
831 chuck 1.1.2.17 return false;
832 }
833
|
834 chuck 1.1.2.20 // Since applyContext has been called, the first chain element
835 // will be the FROM class, so go to the 2nd chain element.
|
836 chuck 1.1.2.17 if (ids[1].isScoped())
837 {
|
838 chuck 1.1.2.20 // The 2nd chain element is a scoped property.
839 // Eg. fromclass.someclass::someprop
|
840 chuck 1.1.2.5
|
841 chuck 1.1.2.20 // Determine the class that the property is being scoped to.
842 // This could be the FROM class, or some other class not in the FROM list
843 CIMName scopingClass = CIMName(ids[1].getScope());
844
845 // Check if the scoping class is the same as the class passed in.
846 if (scopingClass == className)
|
847 chuck 1.1.2.5 {
|
848 chuck 1.1.2.20 // The scoping class is the same as the class passed,
849 // add the property if not already added
850 if (!containsProperty(ids[1].getName(), reqProps))
851 {
852 reqProps.append(ids[1].getName());
853 }
854 }
855 else
856 {
857 // The scoping class is not the same as the class passed.
858 // Check if we already know that the scoping class is a subclass
859 // of the class passed in
860 if (containsProperty(scopingClass, unmatchedScopes))
|
861 chuck 1.1.2.5 {
|
862 chuck 1.1.2.20 // Scoping class is a subclass.
863 return false;
864 }
865
866 // Check if we already know that the scoping class is a superclass
867 // of the class passed in
868 Boolean isSuper = false;
869 if (containsProperty(scopingClass, matchedScopes))
|
870 chuck 1.1.2.5 {
|
871 chuck 1.1.2.20 // Scoping class is a superclass.
872 isSuper = true;
873 }
874
875 // Check if the scoping class is a superclass of the class passed in
876 if (isSuper || _ctx->isSubClass(scopingClass, className))
877 {
878 // Scoping class is a superclass of the class passed in.
879 if (!isSuper)
880 {
881 // Save this information
882 matchedScopes.append(scopingClass);
883 }
884
885 // Add to the required property list if not already there.
|
886 chuck 1.1.2.17 if (!containsProperty(ids[1].getName(), reqProps))
887 {
888 reqProps.append(ids[1].getName());
889 }
|
890 chuck 1.1.2.5 }
|
891 chuck 1.1.2.20 else
892 {
893 // Scoping class is not superclass of class passed in.
894 // Save this information.
895 unmatchedScopes.append(scopingClass);
896 }
897 } // end else scoping class not == class passed in
898 } // end if first id is scoped
|
899 chuck 1.1.2.5 else
900 {
|
901 chuck 1.1.2.20 // The 2nd chain element is an unscoped property
|
902 chuck 1.1.2.23 // Check if it is wildcarded
|
903 chuck 1.1.2.5 if (ids[1].isWildcard())
904 {
|
905 chuck 1.1.2.23 // Wildcard.
906 // If the class passed in is the FROM class, then
907 // all properties are required on the class passed in.
908 CIMName fromClassName = _ctx->getFromList()[0].getName();
909 if (fromClassName == className)
910 {
|
911 chuck 1.1.2.22 return true;
|
912 chuck 1.1.2.23 }
913
914 // Add all the properties on the FROM class to
915 // the required property list.
916 CIMClass fromClass = _ctx->getClass(fromClassName);
917 for (Uint32 n = 0; n < fromClass.getPropertyCount(); n++)
918 {
919 // Add to the required property list if not already there.
920 if (!containsProperty(fromClass.getProperty(n).getName(), reqProps))
921 {
922 reqProps.append(fromClass.getProperty(n).getName());
923 }
924 }
925
926 return false;
|
927 chuck 1.1.2.5 }
928
|
929 chuck 1.1.2.20 // Implementation note:
930 // Since this API assumes that the class passed in
931 // is the FROM class or a subclass of the FROM class,
932 // AND validateProperties can be called to check if
933 // unscoped properties are on the FROM class,
934 // we can just add the required property because
935 // it is assumed to be on the FROM class.
936
937 // Add to the required property list if not already there.
938 if (!containsProperty(ids[1].getName(), reqProps))
939 {
940 reqProps.append(ids[1].getName());
|
941 chuck 1.1.2.5 }
942 }
943
|
944 chuck 1.1.2.20 // Indicate the required property is not a wildcard
|
945 chuck 1.1.2.5 return false;
946 }
947
948 Boolean CQLSelectStatementRep::containsProperty(const CIMName& name,
|
949 chuck 1.1.2.17 const Array<CIMName>& props)
|
950 chuck 1.1.2.5 {
951 for (Uint32 i = 0; i < props.size(); i++)
952 {
953 if (props[i] == name)
954 {
955 return true;
956 }
957 }
958
959 return false;
960 }
961
|
962 humberto 1.1.2.1 void CQLSelectStatementRep::appendClassPath(const CQLIdentifier& inIdentifier)
963 {
|
964 chuck 1.1.2.3 _ctx->insertClassPath(inIdentifier);
|
965 humberto 1.1.2.1 }
966
|
967 humberto 1.1.2.18 void CQLSelectStatementRep::setPredicate(const CQLPredicate& inPredicate)
|
968 humberto 1.1.2.1 {
|
969 chuck 1.1.2.3 _predicate = inPredicate;
|
970 humberto 1.1.2.1 }
971
|
972 chuck 1.1.2.16 void CQLSelectStatementRep::insertClassPathAlias(const CQLIdentifier& inIdentifier,
|
973 chuck 1.1.2.17 String inAlias)
|
974 humberto 1.1.2.1 {
|
975 chuck 1.1.2.3 _ctx->insertClassPath(inIdentifier,inAlias);
|
976 humberto 1.1.2.1 }
977
978 void CQLSelectStatementRep::appendSelectIdentifier(const CQLChainedIdentifier& x)
979 {
|
980 chuck 1.1.2.3 _selectIdentifiers.append(x);
|
981 humberto 1.1.2.1 }
982
|
983 chuck 1.1.2.5 void CQLSelectStatementRep::applyContext()
984 {
985 for (Uint32 i = 0; i < _selectIdentifiers.size(); i++)
986 {
987 _selectIdentifiers[i].applyContext(*_ctx);
|
988 chuck 1.1.2.9 checkWellFormedIdentifier(_selectIdentifiers[i], true);
|
989 chuck 1.1.2.5 }
990
991 if (hasWhereClause())
|
992 chuck 1.1.2.9 {
|
993 chuck 1.1.2.5 _predicate.applyContext(*_ctx);
|
994 chuck 1.1.2.9
|
995 chuck 1.1.2.23 // Note: must be after call to predicate's applyContext
996 Array<CQLChainedIdentifier> _whereIdentifiers = _ctx->getWhereList();
997 for (Uint32 i = 0; i < _whereIdentifiers.size(); i++)
998 {
999 checkWellFormedIdentifier(_whereIdentifiers[i], false);
1000 }
|
1001 chuck 1.1.2.17 }
|
1002 chuck 1.1.2.9 }
1003
1004 void CQLSelectStatementRep::checkWellFormedIdentifier(const CQLChainedIdentifier& chainId,
|
1005 chuck 1.1.2.17 Boolean isSelectListId)
|
1006 chuck 1.1.2.9 {
|
1007 chuck 1.1.2.17 // This function assumes that applyContext has been called.
|
1008 chuck 1.1.2.9 Array<CQLIdentifier> ids = chainId.getSubIdentifiers();
1009
|
1010 chuck 1.1.2.17 if (ids.size() == 0)
1011 {
1012 throw Exception("TEMP MSG: empty chained identifier");
1013 }
|
1014 chuck 1.1.2.9
|
1015 chuck 1.1.2.17 if (ids.size() == 1 && isSelectListId)
|
1016 chuck 1.1.2.9 {
|
1017 chuck 1.1.2.20 // Single element chain ids are not allow in the select list.
1018 // The select list can only have properties.
|
1019 chuck 1.1.2.17 throw Exception("TEMP MSG: need to select a property on the FROM class");
|
1020 chuck 1.1.2.9 }
1021
|
1022 chuck 1.1.2.17 if (ids[0].isScoped()
1023 || ids[0].isWildcard()
1024 || ids[0].isSymbolicConstant()
1025 || ids[0].isArray())
1026 {
|
1027 chuck 1.1.2.20 // The first identifier must be a classname (it could be the FROM class, or
1028 // some other class for the right side of ISA)
|
1029 chuck 1.1.2.17 throw Exception("TEMP MSG: first identifier is illegal after applyContext");
1030 }
1031
1032 Uint32 startingPos = 1;
|
1033 chuck 1.1.2.9 for (Uint32 pos = startingPos; pos < ids.size(); pos++)
1034 {
1035 if (ids[pos].isArray() && isSelectListId)
1036 {
1037 throw Exception("TEMP MSG: array indexing not allowed in basic select");
1038 }
1039
1040 if (ids[pos].isSymbolicConstant() && isSelectListId)
1041 {
|
1042 chuck 1.1.2.17 throw Exception("TEMP MSG: symbolic constant not allowed in basic select");
|
1043 chuck 1.1.2.9 }
1044
|
1045 chuck 1.1.2.20 if (ids[pos].isSymbolicConstant() && pos != (ids.size() -1))
1046 {
1047 throw Exception("TEMP MSG: symbolic constant must be last chain element");
1048 }
1049
|
1050 chuck 1.1.2.9 if (ids[pos].isWildcard())
1051 {
1052 if ( !isSelectListId)
1053 {
|
1054 chuck 1.1.2.17 throw Exception("TEMP MSG: wildcard not allowed in WHERE clause");
|
1055 chuck 1.1.2.9 }
1056
|
1057 chuck 1.1.2.16 if ( pos != ids.size() - 1)
|
1058 chuck 1.1.2.9 {
|
1059 chuck 1.1.2.17 throw Exception("TEMP MSG: wildcard must be at the end of select-list property");
|
1060 chuck 1.1.2.9 }
1061 }
1062
1063 if (pos > startingPos && !ids[pos].isWildcard())
1064 {
1065 if (!ids[pos].isScoped())
1066 {
|
1067 chuck 1.1.2.17 throw Exception("TEMP MSG: property on embedded object must be scoped");
|
1068 chuck 1.1.2.9 }
1069 }
1070 }
|
1071 chuck 1.1.2.5 }
1072
1073 void CQLSelectStatementRep::normalizeToDOC()
1074 {
1075
1076 }
1077
1078 String CQLSelectStatementRep::toString()
1079 {
|
1080 chuck 1.1.2.17 String s("SELECT ");
1081 for(Uint32 i = 0; i < _selectIdentifiers.size(); i++){
1082 if((i > 0) && (i < _selectIdentifiers.size())){
1083 s.append(",");
1084 }
1085 s.append(_selectIdentifiers[i].toString());
1086 }
1087
1088 s.append(" ");
1089 s.append(_ctx->getFromString());
1090
1091 if(_hasWhereClause){
|
1092 chuck 1.1.2.20 s.append(" WHERE ");
|
1093 chuck 1.1.2.17 s.append(_predicate.toString());
1094 }
1095 return s;
|
1096 chuck 1.1.2.4 }
1097
|
1098 chuck 1.1.2.5 void CQLSelectStatementRep::setHasWhereClause()
1099 {
|
1100 chuck 1.1.2.4 _hasWhereClause = true;
1101 }
1102
|
1103 chuck 1.1.2.5 Boolean CQLSelectStatementRep::hasWhereClause()
1104 {
|
1105 chuck 1.1.2.4 return _hasWhereClause;
1106 }
1107
|
1108 chuck 1.1.2.5 void CQLSelectStatementRep::clear()
1109 {
|
1110 chuck 1.1.2.17 _ctx->clear();
1111 _hasWhereClause = false;
1112 _contextApplied = false;
1113 _predicate = CQLPredicate();
1114 _selectIdentifiers.clear();
|
1115 chuck 1.1.2.4 }
1116
|
1117 humberto 1.1.2.1 PEGASUS_NAMESPACE_END
|