2 chip 1.1 //
3 // Copyright (c) 2000, 2001 The Open group, BMC Software, Tivoli Systems, IBM
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to
7 // deal in the Software without restriction, including without limitation the
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 // sell copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
13 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
14 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
15 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
16 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
17 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 //
21 //==============================================================================
22 //
|
43 kumpf 1.2
44 #define PEGASUS_ARRAY_T KeyBinding
45 # include "ArrayImpl.h"
46 #undef PEGASUS_ARRAY_T
47
48 #define PEGASUS_ARRAY_T CIMObjectPath
49 # include "ArrayImpl.h"
50 #undef PEGASUS_ARRAY_T
51
52 // ATTN: KS May 2002 P0 Add resolve method to CIMObjectPath.
53 // Add a resolve method to this class to verify that the
54 // reference is correct (that the class name corresponds to a real
55 // class and that the property names are really keys and that all keys
56 // of the class or used. Also be sure that there is a valid conversion
57 // between the string value and the value of that property).
58 //
59 // ATTN: also check to see that the reference refers to a class that is the
60 // same or derived from the _className member.
61
62 ////////////////////////////////////////////////////////////////////////////////
63 //
64 kumpf 1.2 // Local routines:
65 //
66 ////////////////////////////////////////////////////////////////////////////////
67
68 static String _escapeSpecialCharacters(const String& str)
69 {
70 String result;
71
72 for (Uint32 i = 0, n = str.size(); i < n; i++)
73 {
74 switch (str[i])
75 {
76 case '\n':
77 result += "\\n";
78 break;
79
80 case '\r':
81 result += "\\r";
82 break;
83
84 case '\t':
85 kumpf 1.2 result += "\\t";
86 break;
87
88 case '"':
89 result += "\\\"";
90 break;
91
92 default:
93 result += str[i];
94 }
95 }
96
97 return result;
98 }
99
100 static void _BubbleSort(Array<KeyBinding>& x)
101 {
102 Uint32 n = x.size();
103
104 if (n < 2)
105 return;
106 kumpf 1.2
107 for (Uint32 i = 0; i < n - 1; i++)
108 {
109 for (Uint32 j = 0; j < n - 1; j++)
110 {
111 if (String::compareNoCase(x[j].getName(), x[j+1].getName()) > 0)
112 {
113 KeyBinding t = x[j];
114 x[j] = x[j+1];
115 x[j+1] = t;
116 }
117 }
118 }
119 }
120
121 ////////////////////////////////////////////////////////////////////////////////
122 //
123 // KeyBinding
124 //
125 ////////////////////////////////////////////////////////////////////////////////
126
127 kumpf 1.2 class KeyBindingRep
128 {
129 public:
130 KeyBindingRep()
131 {
132 }
133
134 KeyBindingRep(const KeyBindingRep& x)
135 : _name(x._name), _value(x._value), _type(x._type)
136 {
137 }
138
139 KeyBindingRep(
140 const String& name,
141 const String& value,
142 KeyBinding::Type type)
143 : _name(name), _value(value), _type(type)
144 {
145 }
146
147 ~KeyBindingRep()
148 kumpf 1.2 {
149 }
150
151 KeyBindingRep& operator=(const KeyBindingRep& x)
152 {
153 if (&x != this)
154 {
155 _name = x._name;
156 _value = x._value;
157 _type = x._type;
158 }
159 return *this;
160 }
161
162 String _name;
163 String _value;
164 KeyBinding::Type _type;
165 };
166
167
168 KeyBinding::KeyBinding()
169 kumpf 1.2 {
170 _rep = new KeyBindingRep();
171 }
172
173 KeyBinding::KeyBinding(const KeyBinding& x)
174 {
175 _rep = new KeyBindingRep(*x._rep);
176 }
177
178 KeyBinding::KeyBinding(const String& name, const String& value, Type type)
179 {
180 _rep = new KeyBindingRep(name, value, type);
181 }
182
183 KeyBinding::~KeyBinding()
184 {
185 delete _rep;
186 }
187
188 KeyBinding& KeyBinding::operator=(const KeyBinding& x)
189 {
190 kumpf 1.2 *_rep = *x._rep;
191 return *this;
192 }
193
194 const String& KeyBinding::getName() const
195 {
196 return _rep->_name;
197 }
198
199 void KeyBinding::setName(const String& name)
200 {
201 _rep->_name = name;
202 }
203
204 const String& KeyBinding::getValue() const
205 {
206 return _rep->_value;
207 }
208
209 void KeyBinding::setValue(const String& value)
210 {
211 kumpf 1.2 _rep->_value = value;
212 }
213
214 KeyBinding::Type KeyBinding::getType() const
215 {
216 return _rep->_type;
217 }
218
219 void KeyBinding::setType(KeyBinding::Type type)
220 {
221 _rep->_type = type;
222 }
223
224 const char* KeyBinding::typeToString(KeyBinding::Type type)
225 {
226 switch (type)
227 {
228 case KeyBinding::BOOLEAN:
229 return "boolean";
230
231 case KeyBinding::STRING:
232 kumpf 1.2 return "string";
233
234 case KeyBinding::NUMERIC:
235 return "numeric";
236
237 case KeyBinding::REFERENCE:
238 default:
239 PEGASUS_ASSERT(false);
240 }
241
242 return "unknown";
243 }
244
245 Boolean operator==(const KeyBinding& x, const KeyBinding& y)
246 {
247 return
248 CIMName::equal(x.getName(), y.getName()) &&
249 String::equal(x.getValue(), y.getValue()) &&
250 x.getType() == y.getType();
251 }
252
253 kumpf 1.2
254 ////////////////////////////////////////////////////////////////////////////////
255 //
256 // CIMObjectPath
257 //
258 ////////////////////////////////////////////////////////////////////////////////
259
260 class CIMObjectPathRep
261 {
262 public:
263 CIMObjectPathRep()
264 {
265 }
266
267 CIMObjectPathRep(const CIMObjectPathRep& x)
268 : _host(x._host), _nameSpace(x._nameSpace),
269 _className(x._className), _keyBindings(x._keyBindings)
270 {
271 }
272
273 CIMObjectPathRep(
274 kumpf 1.2 const String& host,
275 const String& nameSpace,
276 const String& className,
277 const Array<KeyBinding>& keyBindings)
278 : _host(host), _nameSpace(nameSpace),
279 _className(className), _keyBindings(keyBindings)
280 {
281 }
282
283 ~CIMObjectPathRep()
284 {
285 }
286
287 CIMObjectPathRep& operator=(const CIMObjectPathRep& x)
288 {
289 if (&x != this)
290 {
291 _host = x._host;
292 _nameSpace = x._nameSpace;
293 _className = x._className;
294 _keyBindings = x._keyBindings;
295 kumpf 1.2 }
296 return *this;
297 }
298
299 //
300 // Contains port as well (e.g., myhost:1234).
301 //
302 String _host;
303
304 String _nameSpace;
305 String _className;
306 Array<KeyBinding> _keyBindings;
307 };
308
309
310 CIMObjectPath::CIMObjectPath()
311 {
312 _rep = new CIMObjectPathRep();
313 }
314
315 CIMObjectPath::CIMObjectPath(const CIMObjectPath& x)
316 kumpf 1.2 {
317 _rep = new CIMObjectPathRep(*x._rep);
318 }
319
320 CIMObjectPath::CIMObjectPath(const String& objectName)
321 {
322 // Test the objectName out to see if we get an exception
323 CIMObjectPath tmpRef;
324 tmpRef.set(objectName);
325
326 _rep = new CIMObjectPathRep(*tmpRef._rep);
327 }
328
329 CIMObjectPath::CIMObjectPath(const char* objectName)
330 {
331 // Test the objectName out to see if we get an exception
332 CIMObjectPath tmpRef;
333 tmpRef.set(objectName);
334
335 _rep = new CIMObjectPathRep(*tmpRef._rep);
336 }
337 kumpf 1.2
338 CIMObjectPath::CIMObjectPath(
339 const String& host,
340 const String& nameSpace,
341 const String& className,
342 const Array<KeyBinding>& keyBindings)
343 {
344 // Test the objectName out to see if we get an exception
345 CIMObjectPath tmpRef;
346 tmpRef.set(host, nameSpace, className, keyBindings);
347
348 _rep = new CIMObjectPathRep(*tmpRef._rep);
349 }
350
351 CIMObjectPath::~CIMObjectPath()
352 {
353 delete _rep;
354 }
355
356 CIMObjectPath& CIMObjectPath::operator=(const CIMObjectPath& x)
357 {
358 kumpf 1.2 *_rep = *x._rep;
359 return *this;
360 }
361
362 void CIMObjectPath::clear()
363 {
364 _rep->_host.clear();
365 _rep->_nameSpace.clear();
366 _rep->_className.clear();
367 _rep->_keyBindings.clear();
368 }
369
370 void CIMObjectPath::set(
371 const String& host,
372 const String& nameSpace,
373 const String& className,
374 const Array<KeyBinding>& keyBindings) throw(IllformedObjectName, IllegalName)
375 {
376 setHost(host);
377 setNameSpace(nameSpace);
378 setClassName(className);
379 kumpf 1.2 setKeyBindings(keyBindings);
380 }
381
382 Boolean CIMObjectPath::_parseHostElement(
383 const String& objectName,
384 char*& p,
385 String& host) throw(IllformedObjectName)
386 {
387 // See if there is a host name (true if it begins with "//"):
388 // Host is of the from <hostname>-<port> and begins with "//"
389 // and ends with "/":
390
391 if (p[0] != '/' || p[1] != '/')
392 {
393 return false;
394 }
395
396 p += 2;
397
398 //----------------------------------------------------------------------
399 // Validate the hostname. Hostnames must match the following
400 kumpf 1.2 // regular expression: "[A-Za-z][A-Za-z0-9-]*"
401 //----------------------------------------------------------------------
402
403 char* q = p;
404
405 if (!isalpha(*q))
406 throw IllformedObjectName(objectName);
407
408 q++;
409
410 while (isalnum(*q) || *q == '-')
411 q++;
412
413 // We now expect a port (or default the port).
414
415 if (*q == ':')
416 {
417 q++;
418 // Check for a port number:
419
420 if (!isdigit(*q))
421 kumpf 1.2 throw IllformedObjectName(objectName);
422
423 while (isdigit(*q))
424 q++;
425
426 // Finally, assign the host name:
427
428 host.assign(p, q - p);
429 }
430 else
431 {
432 host.assign(p, q - p);
433
434 // Assign the default port number:
435
436 host.append(":");
437 host.append(PEGASUS_CIMOM_DEFAULT_PORT_STRING);
438 }
439
440 // Check for slash terminating the entire sequence:
441
442 kumpf 1.2 if (*q != '/')
443 {
444 host.clear();
445 throw IllformedObjectName(objectName);
446 }
447
448 p = ++q;
449
450 return true;
451 }
452
453 Boolean CIMObjectPath::_parseNamespaceElement(
454 const String& objectName,
455 char*& p,
456 String& nameSpace)
457 {
458 // If we don't find a valid namespace name followed by a ':', we
459 // assume we're not looking at a namespace name.
460
461 //----------------------------------------------------------------------
462 // Validate the namespace path. Namespaces must match the following
463 kumpf 1.2 // regular expression: "[A-Za-z_]+(/[A-Za-z_]+)*"
464 //----------------------------------------------------------------------
465
466 char* q = p;
467
468 for (;;)
469 {
470 // Pass the next token:
471
472 if (!*q || (!isalpha(*q) && *q != '_'))
473 return false;
474
475 q++;
476
477 while (isalnum(*q) || *q == '_')
478 q++;
479
480 if (!*q)
481 return false;
482
483 if (*q == ':')
484 kumpf 1.2 break;
485
486 if (*q == '/')
487 {
488 q++;
489 continue;
490 }
491
492 return false;
493 }
494
495 nameSpace.assign(p, q - p);
496 p = ++q;
497 return true;
498 }
499
500 /**
501 ATTN: RK - Association classes have keys whose types are
502 references. These reference values must be treated specially
503 in the XML encoding, using the VALUE.REFERENCE tag structure.
504
505 kumpf 1.2 Pegasus had been passing reference values simply as String
506 values. For example, EnumerateInstanceNames returned
507 KEYVALUEs of string type rather than VALUE.REFERENCEs.
508
509 I've modified the XmlReader::getKeyBindingElement() and
510 XmlWriter::appendInstanceNameElement() methods to read and write
511 the XML in the proper format. However, making that change
512 required that a CIMObjectPath object be able to distinguish
513 between a key of String type and a key of reference type.
514
515 I've modified the String format of CIMObjectPaths slightly to
516 allow efficient processing of references whose keys are also
517 of reference type. The "official" form uses the same
518 encoding for key values of String type and of reference type,
519 and so it would be necessary to retrieve the class definition
520 and look up the types of the key properties to determine how
521 to treat the key values. This is clearly too inefficient for
522 internal transformations between CIMObjectPaths and String
523 values.
524
525 The workaround is to encode a 'R' at the beginning of the
526 kumpf 1.2 value for a key of reference type (before the opening '"').
527 This allows the parser to know a priori whether the key is of
528 String or reference type.
529
530 In this example:
531
532 MyClass.Key1="StringValue",Key2=R"RefClass.KeyA="StringA",KeyB=10"
533
534 Property Key1 of class MyClass is of String type, and so it
535 gets the usual encoding. Key2 is a reference property, so
536 the extra 'R' is inserted before its encoded value. Note
537 that this algorithm is recursive, such that RefClass could
538 include KeyC of reference type, which would also get encoded
539 with the 'R' notation.
540
541 The toString() method inserts the 'R' to provide symmetry. A
542 new KeyBinding type (REFERENCE) has been defined to denote
543 keys in a CIMObjectPath that are of reference type. This
544 KeyBinding type must be used appropriately for
545 CIMObjectPath::toString() to behave correctly.
546
547 kumpf 1.2 A result of this change is that instances names in the
548 instance repository will include this extra 'R' character.
549 Note that for user-facing uses of the String encoding of
550 instance names (such as might appear in MOF for static
551 association instances or in the CGI client), this solution
552 is non-standard and therefore unacceptable. It is likely
553 that these points will need to process the more expensive
554 operation of retrieving the class definition to determine
555 the key property types.
556 */
557 void CIMObjectPath::_parseKeyBindingPairs(
558 const String& objectName,
559 char*& p,
560 Array<KeyBinding>& keyBindings) throw(IllformedObjectName)
561 {
562 // Get the key-value pairs:
563
564 while (*p)
565 {
566 // Get key part:
567
|
584 kumpf 1.2 KeyBinding::Type type;
585
586 if (*p == 'R')
587 {
588 p++;
589
590 type = KeyBinding::REFERENCE;
591
592 if (*p++ != '"')
593 throw IllformedObjectName(objectName);
594
595 while (*p && *p != '"')
596 {
597 // ATTN: need to handle special characters here:
598
599 if (*p == '\\')
600 *p++;
601
602 valueString.append(*p++);
603 }
604
605 kumpf 1.2 if (*p++ != '"')
606 throw IllformedObjectName(objectName);
607 }
608 else if (*p == '"')
609 {
610 p++;
611
612 type = KeyBinding::STRING;
613
614 while (*p && *p != '"')
615 {
616 // ATTN: need to handle special characters here:
617
618 if (*p == '\\')
619 *p++;
620
621 valueString.append(*p++);
622 }
623
624 if (*p++ != '"')
625 throw IllformedObjectName(objectName);
626 kumpf 1.2 }
627 else if (toupper(*p) == 'T' || toupper(*p) == 'F')
628 {
629 type = KeyBinding::BOOLEAN;
630
631 char* r = p;
632 Uint32 n = 0;
633
634 while (*r && *r != ',')
635 {
636 *r = toupper(*r);
637 r++;
638 n++;
639 }
640
641 if (!(((strncmp(p, "TRUE", n) == 0) && n == 4) ||
642 ((strncmp(p, "FALSE", n) == 0) && n == 5)))
643 throw IllformedObjectName(objectName);
644
645 valueString.assign(p, n);
646
647 kumpf 1.2 p = p + n;
648 }
649 else
650 {
651 type = KeyBinding::NUMERIC;
652
653 char* r = p;
654 Uint32 n = 0;
655
656 while (*r && *r != ',')
657 {
658 r++;
659 n++;
660 }
661
662 Boolean isComma = false;
663 if (*r)
664 {
665 *r = '\0';
666 isComma = true;
667 }
668 kumpf 1.2
669 Sint64 x;
670
671 if (!XmlReader::stringToSignedInteger(p, x))
672 throw IllformedObjectName(objectName);
673
674 valueString.assign(p, n);
675
676 if (isComma)
677 {
678 *r = ',';
679 }
680
681 p = p + n;
682 }
683
684 keyBindings.append(KeyBinding(keyString, valueString, type));
685
686 if (*p)
687 {
688 if (*p++ != ',')
689 kumpf 1.2 {
690 throw IllformedObjectName(objectName);
691 }
692 }
693 }
694
695 _BubbleSort(keyBindings);
696 }
697
698 void CIMObjectPath::set(const String& objectName) throw(IllformedObjectName)
699 {
700 clear();
701
702 //--------------------------------------------------------------------------
703 // We will extract components from an object name. Here is an sample
704 // object name:
705 //
706 // //atp:9999/root/cimv25:TennisPlayer.first="Patrick",last="Rafter"
707 //--------------------------------------------------------------------------
708
709 // Convert to a C String first:
710 kumpf 1.2
|
712 kumpf 1.2 ArrayDestroyer<char> destroyer(p);
713 Boolean gotHost;
714 Boolean gotNamespace;
715
716 gotHost = _parseHostElement(objectName, p, _rep->_host);
717 gotNamespace = _parseNamespaceElement(objectName, p, _rep->_nameSpace);
718
719 if (gotHost && !gotNamespace)
720 {
721 throw IllformedObjectName(objectName);
722 }
723
724 // Extract the class name:
725
726 char* dot = strchr(p, '.');
727
728 if (!dot)
729 {
730 if (!CIMName::legal(p))
731 {
732 throw IllformedObjectName(objectName);
733 kumpf 1.2 }
734
735 // ATTN: remove this later: a reference should only be able to hold
736 // an instance name.
737
738 _rep->_className.assign(p);
739 return;
740 }
741
742 _rep->_className.assign(p, dot - p);
743
744 // Advance past dot:
745
746 p = dot + 1;
747
748 _parseKeyBindingPairs(objectName, p, _rep->_keyBindings);
749 }
750
751 CIMObjectPath& CIMObjectPath::operator=(const String& objectName)
752 {
753 set(objectName);
754 kumpf 1.2 return *this;
755 }
756
757 CIMObjectPath& CIMObjectPath::operator=(const char* objectName)
758 {
759 set(objectName);
760 return *this;
761 }
762
763 const String& CIMObjectPath::getHost() const
764 {
765 return _rep->_host;
766 }
767
768 void CIMObjectPath::setHost(const String& host)
769 {
770 _rep->_host = host;
771 }
772
773 const String& CIMObjectPath::getNameSpace() const
774 {
775 kumpf 1.2 return _rep->_nameSpace;
776 }
777
778 void CIMObjectPath::setNameSpace(const String& nameSpace) throw(IllegalName)
779 {
780 String temp;
781
782 // check each namespace segment (delimted by '/') for correctness
783
784 for(Uint32 i = 0; i < nameSpace.size(); i += temp.size() + 1)
785 {
786 // isolate the segment beginning at i and ending at the first
787 // ocurrance of '/' after i or eos
788
789 temp = nameSpace.subString(i, nameSpace.subString(i).find('/'));
790
791 // check segment for correctness
792
793 if(!CIMName::legal(temp))
794 {
795 throw IllegalName() ;
796 kumpf 1.2 }
797 }
798
799 _rep->_nameSpace = nameSpace;
800 }
801
802 const String& CIMObjectPath::getClassName() const
803 {
804 return _rep->_className;
805 }
806
807 void CIMObjectPath::setClassName(const String& className) throw(IllegalName)
808 {
809 if (!CIMName::legal(className))
810 throw IllegalName();
811
812 _rep->_className = className;
813 }
814
815 const Array<KeyBinding>& CIMObjectPath::getKeyBindings() const
816 {
817 kumpf 1.2 return _rep->_keyBindings;
818 }
819
820 void CIMObjectPath::setKeyBindings(const Array<KeyBinding>& keyBindings)
821 {
822 _rep->_keyBindings = keyBindings;
823 _BubbleSort(_rep->_keyBindings);
824 }
825
826 String CIMObjectPath::toString(Boolean includeHost) const
827 {
828 String objectName;
829
830 // Get the host:
831
832 if (_rep->_host.size() && includeHost)
833 {
834 objectName = "//";
835 objectName += _rep->_host;
836 objectName += "/";
837 }
838 kumpf 1.2
839 // Get the namespace (if we have a host name, we must write namespace):
840
841 if (_rep->_nameSpace.size() || _rep->_host.size())
842 {
843 objectName += _rep->_nameSpace;
844 objectName += ":";
845 }
846
847 // Get the class name:
848
849 objectName.append(getClassName());
850
851 if (isInstanceName())
852 {
853 objectName.append('.');
854
855 // Append each key-value pair:
856
857 const Array<KeyBinding>& keyBindings = getKeyBindings();
858
859 kumpf 1.2 for (Uint32 i = 0, n = keyBindings.size(); i < n; i++)
860 {
861 objectName.append(keyBindings[i].getName());
862 objectName.append('=');
863
864 const String& value = _escapeSpecialCharacters(
865 keyBindings[i].getValue());
866
867 KeyBinding::Type type = keyBindings[i].getType();
868
869 if (type == KeyBinding::REFERENCE)
870 objectName.append('R');
871
872 if (type == KeyBinding::STRING || type == KeyBinding::REFERENCE)
873 objectName.append('"');
874
875 objectName.append(value);
876
877 if (type == KeyBinding::STRING || type == KeyBinding::REFERENCE)
878 objectName.append('"');
879
880 kumpf 1.2 if (i + 1 != n)
881 objectName.append(',');
882 }
883 }
884
885 return objectName;
886 }
887
888 String CIMObjectPath::toStringCanonical(Boolean includeHost) const
889 {
890 CIMObjectPath ref = *this;
891
892 // ATTN-RK-P2-20020510: Need to make hostname and namespace lower case?
893
894 ref._rep->_className.toLower();
895
896 for (Uint32 i = 0, n = ref._rep->_keyBindings.size(); i < n; i++)
897 {
898 ref._rep->_keyBindings[i]._rep->_name.toLower();
899 }
900
901 kumpf 1.2 return ref.toString(includeHost);
902 }
903
904 CIMObjectPath CIMObjectPath::clone() const
905 {
906 return CIMObjectPath(*this);
907 }
908
909 Boolean CIMObjectPath::identical(const CIMObjectPath& x) const
910 {
911 return
912 String::equal(_rep->_host, x._rep->_host) &&
913 CIMName::equal(_rep->_nameSpace, x._rep->_nameSpace) &&
914 CIMName::equal(_rep->_className, x._rep->_className) &&
915 _rep->_keyBindings == x._rep->_keyBindings;
916 }
917
918 Uint32 CIMObjectPath::makeHashCode() const
919 {
920 return HashFunc<String>::hash(toStringCanonical());
921 }
922 kumpf 1.2
923 Boolean CIMObjectPath::isInstanceName() const
924 {
925 return _rep->_keyBindings.size() != 0;
926 }
927
928 KeyBindingArray CIMObjectPath::getKeyBindingArray()
929 {
930 return KeyBindingArray();
931 }
932
933
934 Boolean operator==(const CIMObjectPath& x, const CIMObjectPath& y)
935 {
936 return x.identical(y);
937 }
938
939 Boolean operator!=(const CIMObjectPath& x, const CIMObjectPath& y)
940 {
941 return !operator==(x, y);
942 }
943 kumpf 1.2
944 PEGASUS_STD(ostream)& operator<<(
945 PEGASUS_STD(ostream)& os,
946 const CIMObjectPath& x)
947 {
948 return os << x.toString();
949 }
|