remove unecessary bubblesort in set()
[tpot/pegasus/.git] / src / Pegasus / Common / CIMReference.cpp
1 //%/////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2000 The Open Group, BMC Software, Tivoli Systems, IBM
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
15 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18 // DEALINGS IN THE SOFTWARE.
19 //
20 //==============================================================================
21 //
22 // Author: Mike Brasher (mbrasher@bmc.com)
23 //
24 // Modified By:
25 //
26 //%/////////////////////////////////////////////////////////////////////////////
27
28 #include <cctype>
29 #include <cstring>
30 #include "HashTable.h"
31 #include "CIMReference.h"
32 #include "Indentor.h"
33 #include "CIMName.h"
34 #include "Destroyer.h"
35 #include "XmlWriter.h"
36 #include "XmlReader.h"
37 #include "Array.h"
38
39 PEGASUS_NAMESPACE_BEGIN
40
41 #define PEGASUS_ARRAY_T CIMReference
42 # include "ArrayImpl.h"
43 #undef PEGASUS_ARRAY_T
44
45 #define PEGASUS_ARRAY_T KeyBinding
46 # include "ArrayImpl.h"
47 #undef PEGASUS_ARRAY_T
48
49 // ATTN: add a resolve method to this class to verify that the
50 // reference is correct (that the class name corresponds to a real
51 // class and that the property names are really keys and that all keys
52 // of the class or used. Also be sure that there is a valid conversion
53 // between the string value and the value of that property.
54
55 static String _escapeSpecialCharacters(const String& str)
56 {
57     String result;
58
59     for (Uint32 i = 0, n = str.size(); i < n; i++)
60     {
61         switch (str[i])
62         {
63             case '\n':
64                 result += "\\n";
65                 break;
66
67             case '\r':
68                 result += "\\r";
69                 break;
70
71             case '\t':
72                 result += "\\t";
73                 break;
74
75             case '"':
76                 result += "\\\"";
77                 break;
78
79             default:
80                 result += str[i];
81         }
82     }
83
84     return result;
85 }
86
87 ////////////////////////////////////////////////////////////////////////////////
88 //
89 // Local routines:
90 //
91 ////////////////////////////////////////////////////////////////////////////////
92
93 int _Compare(const String& s1_, const String& s2_)
94 {
95     const Char16* s1 = s1_.getData();
96     const Char16* s2 = s2_.getData();
97
98     while (*s1 && *s2)
99     {
100         char c1 = tolower(*s1++);
101         char c2 = tolower(*s2++);
102         int r = c1 - c2;
103
104         if (r)
105             return r;
106     }
107
108     if (*s2)
109         return -1;
110     else if (*s1)
111         return 1;
112
113     return 0;
114 }
115
116 static void _BubbleSort(Array<KeyBinding>& x)
117 {
118     Uint32 n = x.size();
119
120     if (n < 2)
121         return;
122
123     for (Uint32 i = 0; i < n - 1; i++)
124     {
125         for (Uint32 j = 0; j < n - 1; j++)
126         {
127             if (_Compare(x[j].getName(), x[j+1].getName()) > 0)
128             {
129                 KeyBinding t = x[j];
130                 x[j] = x[j+1];
131                 x[j+1] = t;
132             }
133         }
134     }
135 }
136
137 ////////////////////////////////////////////////////////////////////////////////
138 //
139 // KeyBinding
140 //
141 ////////////////////////////////////////////////////////////////////////////////
142
143 KeyBinding::KeyBinding() { }
144
145 KeyBinding::KeyBinding(const KeyBinding& x)
146     : _name(x._name), _value(x._value), _type(x._type)
147 {
148
149 }
150
151 KeyBinding::KeyBinding(const String& name, const String& value, Type type)
152     : _name(name), _value(value), _type(type)
153 {
154
155 }
156
157 KeyBinding::~KeyBinding()
158 {
159
160 }
161
162 KeyBinding& KeyBinding::operator=(const KeyBinding& x)
163 {
164     _name = x._name;
165     _value = x._value;
166     _type = x._type;
167     return *this;
168 }
169
170 ////////////////////////////////////////////////////////////////////////////////
171 //
172 // CIMReference
173 //
174 ////////////////////////////////////////////////////////////////////////////////
175
176 CIMReference::CIMReference()
177 {
178
179 }
180
181 CIMReference::CIMReference(const CIMReference& x)
182     : _host(x._host), _nameSpace(x._nameSpace),
183     _className(x._className), _keyBindings(x._keyBindings)
184 {
185     _BubbleSort(_keyBindings);
186 }
187
188 CIMReference::CIMReference(const String& objectName)
189 {
190     set(objectName);
191 }
192
193 CIMReference::CIMReference(const char* objectName)
194 {
195     set(objectName);
196 }
197
198 CIMReference::CIMReference(
199     const String& host,
200     const String& nameSpace,
201     const String& className,
202     const Array<KeyBinding>& keyBindings)
203 {
204    set(host, nameSpace, className, keyBindings);
205 }
206
207 CIMReference::~CIMReference()
208 {
209
210 }
211
212 CIMReference& CIMReference::operator=(const CIMReference& x)
213 {
214     if (&x != this)
215     {
216         _host = x._host;
217         _nameSpace = x._nameSpace;
218         _className = x._className;
219         _keyBindings = x._keyBindings;
220     }
221     return *this;
222 }
223
224 void CIMReference::clear()
225 {
226     _host.clear();
227     _nameSpace.clear();
228     _className.clear();
229     _keyBindings.clear();
230 }
231
232 void CIMReference::set(
233     const String& host,
234     const String& nameSpace,
235     const String& className,
236     const Array<KeyBinding>& keyBindings)
237 {
238    setHost(host);
239    setNameSpace(nameSpace);
240    setClassName(className);
241    setKeyBindings(keyBindings);
242 }
243
244 void CIMReference::set(const String& objectName)
245 {
246     _host.clear();
247     _nameSpace.clear();
248     _className.clear();
249     _keyBindings.clear();
250
251     //--------------------------------------------------------------------------
252     // We will extract components from an object name. Here is an sample
253     // object name:
254     //
255     //     //atp:9999/root/cimv25:TennisPlayer.first="Patrick",last="Rafter"
256     //--------------------------------------------------------------------------
257
258     // Convert to a C String first:
259
260     char* p = objectName.allocateCString();
261     ArrayDestroyer<char> destroyer(p);
262
263     // See if there is a host name (true if it begins with "//"):
264     // Host is of the from <hostname>-<port> and begins with "//"
265     // and ends with "/":
266
267     if (p[0] == '/' && p[1] == '/')
268     {
269         p += 2;
270
271         //----------------------------------------------------------------------
272         // Validate the hostname. Hostnames must match the following
273         // regular expression: "[A-Za-z][A-Za-z0-9-]*"
274         //----------------------------------------------------------------------
275
276         char* q = p;
277
278         if (!isalpha(*q))
279             throw IllformedObjectName(objectName);
280
281         q++;
282
283         while (isalnum(*q) || *q == '-')
284             q++;
285
286         // We now expect a colon (before the port):
287
288         if (*q != ':')
289             throw IllformedObjectName(objectName);
290
291         q++;
292
293         // Check for a port number:
294
295         if (!isdigit(*q))
296             throw IllformedObjectName(objectName);
297         
298         while (isdigit(*q))
299             q++;
300
301         // Check for slash terminating the entire sequence:
302
303         if (*q != '/')
304             throw IllformedObjectName(objectName);
305
306         // Finally, assign the host name:
307
308         _host.assign(p, q - p);
309
310         p = ++q;
311
312         //----------------------------------------------------------------------
313         // Validate the namespace path:
314         //----------------------------------------------------------------------
315
316         q = strchr(p, ':');
317
318         if (!q)
319             throw IllformedObjectName(objectName);
320
321         q = p;
322
323         for (;;)
324         {
325             // Pass next next token:
326
327             if (!*q || (!isalpha(*q) && *q != '_'))
328                 throw IllformedObjectName(objectName);
329
330             q++;
331
332             while (isalnum(*q) || *q == '_')
333                 q++;
334
335             if (!*q)
336                 throw IllformedObjectName(objectName);
337
338             if (*q == ':')
339                 break;
340
341             if (*q == '/')
342             {
343                 q++;
344                 continue;
345             }
346
347             throw IllformedObjectName(objectName);
348         }
349
350         _nameSpace.assign(p, q - p);
351         p = ++q;
352     }
353
354     // Extract the class name:
355
356     char* dot = strchr(p, '.');
357
358     if (!dot)
359         throw IllformedObjectName(objectName);
360
361     _className.assign(p, dot - p);
362
363     // Advance past dot:
364
365     p = dot + 1;
366
367     // Get the key-value pairs:
368
369     for (p = strtok(p, ","); p; p = strtok(NULL, ","))
370     {
371         // Split about the equal sign:
372
373         char* equal = strchr(p, '=');
374
375         if (!equal)
376             throw IllformedObjectName(objectName);
377
378         *equal = '\0';
379
380         // Get key part:
381
382         String keyString(p);
383
384         if (!CIMName::legal(keyString))
385             throw IllformedObjectName(objectName);
386
387         // Get the value part:
388
389         String valueString;
390         char* q = equal + 1;
391         KeyBinding::Type type;
392
393         if (*q == '"')
394         {
395             q++;
396
397             type = KeyBinding::STRING;
398
399             while (*q && *q != '"')
400             {
401                 // ATTN: need to handle special characters here:
402
403                 if (*q == '\\')
404                     *q++;
405
406                 valueString.append(*q++);
407             }
408
409             if (*q++ != '"')
410                 throw IllformedObjectName(objectName);
411
412             if (*q)
413                 throw IllformedObjectName(objectName);
414         }
415         else if (toupper(*q) == 'T' || toupper(*q) == 'F')
416         {
417             type = KeyBinding::BOOLEAN;
418
419             char* r = q;
420
421             while (*r)
422             {
423                 *r = toupper(*r);
424                 r++;
425             }
426
427             if (strcmp(q, "TRUE") != 0 && strcmp(q, "FALSE") != 0)
428                 throw IllformedObjectName(objectName);
429
430             valueString.assign(q);
431         }
432         else
433         {
434             type = KeyBinding::NUMERIC;
435
436             Sint64 x;
437
438             if (!XmlReader::stringToSignedInteger(q, x))
439                 throw IllformedObjectName(objectName);
440
441             valueString.assign(q);
442         }
443
444         _keyBindings.append(KeyBinding(keyString, valueString, type));
445     }
446
447     _BubbleSort(_keyBindings);
448 }
449
450 void CIMReference::setNameSpace(const String& nameSpace)
451 {
452    String temp;
453
454    // check each namespace segment (delimted by '\\') for correctness
455    for(Uint32 i = 0; i < nameSpace.size(); i += temp.size() + 1) {
456       // isolate the segment beginning at i and ending at the first ocurrance of '\\' after i or eos
457       temp = nameSpace.subString(i, nameSpace.subString(i).find('\\'));
458
459       // check segment for correctness
460       if(!CIMName::legal(temp)) {
461          throw(IllegalName());
462       }
463    }
464
465    _nameSpace = nameSpace;
466 }
467
468 void CIMReference::setClassName(const String& className)
469 {
470     if (!CIMName::legal(className))
471         throw IllegalName();
472
473     _className = className;
474 }
475
476 void CIMReference::setKeyBindings(const Array<KeyBinding>& keyBindings)
477 {
478     _keyBindings = keyBindings;
479     _BubbleSort(_keyBindings);
480 }
481
482 String CIMReference::toString() const
483 {
484     String objectName;
485
486     // Get the host:
487
488     if (_host.size() && _nameSpace.size())
489     {
490         objectName = "//";
491         objectName += _host;
492         objectName += "/";
493
494         objectName += _nameSpace;
495         objectName += ":";
496     }
497
498     // Get the class name:
499
500     const String& className = getClassName();
501     objectName.append(className);
502     objectName.append('.');
503
504     // Append each key-value pair:
505
506     const Array<KeyBinding>& keyBindings = getKeyBindings();
507
508     for (Uint32 i = 0, n = keyBindings.size(); i < n; i++)
509     {
510         objectName.append(keyBindings[i].getName());
511         objectName.append('=');
512
513         const String& value = _escapeSpecialCharacters(
514             keyBindings[i].getValue());
515
516         KeyBinding::Type type = keyBindings[i].getType();
517         
518         if (type == KeyBinding::STRING)
519             objectName.append('"');
520
521         objectName.append(value);
522
523         if (type == KeyBinding::STRING)
524             objectName.append('"');
525
526         if (i + 1 != n)
527             objectName.append(',');
528     }
529
530     return objectName;
531 }
532
533 String CIMReference::toStringCanonical() const
534 {
535     CIMReference ref = *this;
536
537     ref._className.toLower();
538
539     for (Uint32 i = 0, n = ref._keyBindings.size(); i < n; i++)
540         ref._keyBindings[i]._name.toLower();
541
542     return ref.toString();
543 }
544
545 Boolean CIMReference::identical(const CIMReference& x) const
546 {
547     return
548         String::equal(_host, x._host) &&
549         String::equal(_nameSpace, x._nameSpace) &&
550         CIMName::equal(_className, x._className) &&
551         _keyBindings == x._keyBindings;
552 }
553
554 void CIMReference::nameSpaceToXml(Array<Sint8>& out) const
555 {
556     if (_host.size())
557     {
558         out << "<NAMESPACEPATH>\n";
559         out << "<HOST>" << _host << "</HOST>\n";
560     }
561
562     XmlWriter::appendLocalNameSpaceElement(out, _nameSpace);
563
564     if (_host.size())
565         out << "</NAMESPACEPATH>\n";
566 }
567
568 void CIMReference::localNameSpaceToXml(Array<Sint8>& out) const
569 {
570     out << "<LOCALNAMESPACEPATH>\n";
571
572     char* tmp = _nameSpace.allocateCString();
573
574     for (char* p = strtok(tmp, "/"); p; p = strtok(NULL, "/"))
575     {
576         out << "<NAMESPACE NAME=\"" << p << "\"/>\n";
577     }
578
579     delete [] tmp;
580
581     out << "</LOCALNAMESPACEPATH>\n";
582 }
583
584 void CIMReference::classNameToXml(Array<Sint8>& out) const
585 {
586     out << "<CLASSNAME NAME=\"" << _className << "\"/>\n";
587 }
588
589 void CIMReference::instanceNameToXml(Array<Sint8>& out) const
590 {
591     out << "<INSTANCENAME CLASSNAME=\"" << _className << "\">\n";
592
593     for (Uint32 i = 0, n = _keyBindings.size(); i < n; i++)
594     {
595         out << "<KEYBINDING NAME=\"" << _keyBindings[i].getName() << "\">\n";
596
597         out << "<KEYVALUE VALUETYPE=\"";
598         out << KeyBinding::typeToString(_keyBindings[i].getType());
599         out << "\">";
600
601         out << _keyBindings[i].getValue();
602         out << "</KEYVALUE>\n";
603
604         out << "</KEYBINDING>\n";
605     }
606
607     out << "</INSTANCENAME>\n";
608 }
609
610 void CIMReference::toXml(Array<Sint8>& out) const
611 {
612     out << "<VALUE.REFERENCE>\n";
613
614     // See if it is a class or instance reference (instance references have
615     // key-bindings; class references do not).
616
617     if (_keyBindings.size())
618     {
619         if (_host.size())
620         {
621             out << "<INSTANCEPATH>\n";
622             nameSpaceToXml(out);
623             instanceNameToXml(out);
624             out << "</INSTANCEPATH>\n";
625         }
626         else if (_nameSpace.size())
627         {
628             out << "<LOCALINSTANCEPATH>\n";
629             localNameSpaceToXml(out);
630             instanceNameToXml(out);
631             out << "</LOCALINSTANCEPATH>\n";
632         }
633         else
634             instanceNameToXml(out);
635     }
636     else
637     {
638         if (_host.size())
639         {
640             out << "<CLASSPATH>\n";
641             nameSpaceToXml(out);
642             classNameToXml(out);
643             out << "</CLASSPATH>";
644         }
645         else if (_nameSpace.size())
646         {
647             out << "<LOCALCLASSPATH>\n";
648             nameSpaceToXml(out);
649             classNameToXml(out);
650             out << "</LOCALCLASSPATH>";
651         }
652         else
653             classNameToXml(out);
654     }
655
656     out << "</VALUE.REFERENCE>\n";
657 }
658
659 void CIMReference::print(PEGASUS_STD(ostream)& os) const
660 {
661     Array<Sint8> tmp;
662     toXml(tmp);
663     XmlWriter::indentedPrint(os, tmp.getData());
664 }
665
666 const char* KeyBinding::typeToString(Type type)
667 {
668     switch (type)
669     {
670         case KeyBinding::BOOLEAN:
671             return "boolean";
672
673         case KeyBinding::STRING:
674             return "string";
675
676         case KeyBinding::NUMERIC:
677             return "numeric";
678     }
679
680     return "unknown";
681 }
682
683 Uint32 CIMReference::makeHashCode() const
684 {
685     CIMReference ref = *this;
686
687     ref._className.toLower();
688
689     for (Uint32 i = 0, n = ref._keyBindings.size(); i < n; i++)
690         ref._keyBindings[i]._name.toLower();
691
692     return HashFunc<String>::hash(ref.toString());
693 }
694
695 KeyBindingArray CIMReference::getKeyBindingArray()
696 {
697     return KeyBindingArray();
698 }
699
700 PEGASUS_NAMESPACE_END