Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / tt / slib / mp_s_pattern.C
1 //%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                  
2 //%%  (c) Copyright 1993, 1994 International Business Machines Corp.    
3 //%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                   
4 //%%  (c) Copyright 1993, 1994 Novell, Inc.                             
5 //%%  $XConsortium: mp_s_pattern.C /main/3 1995/10/23 11:57:37 rswiston $                                                       
6 /*
7  * @(#)mp_s_pattern.C   1.27 93/09/07
8  *
9  * mp_s_pattern.cc
10  *
11  * Copyright (c) 1990, 1992 by Sun Microsystems, Inc.
12  */
13 #include "mp_s_global.h"
14 #include "mp/mp_arg.h"
15 #include "mp_s_pat_context.h"
16 #include "mp/mp_mp.h"
17 #include "mp_s_mp.h"
18 #include "mp_s_pattern.h"
19 #include "mp_s_message.h"
20 #include "mp_signature.h"
21 #include "mp_observer.h"
22 #include "mp_ptype.h"
23 #include "mp_rpc_implement.h"
24 #include "mp_s_session.h"
25 #include "mp/mp_xdr_functions.h"
26 #include "util/tt_enumname.h"
27 #include "mp/mp_trace.h"
28 #include "util/tt_global_env.h"
29 #include "util/tt_assert.h"
30
31
32 // 
33 // Creates a pattern from a signature object by extracting the relevant
34 // fields from the signature. 
35 // 
36 _Tt_s_pattern::
37 _Tt_s_pattern(const _Tt_signature_ptr &sig)
38 {
39         _timestamp = 0;
40         set_id(_tt_s_mp->initial_session->address_string());
41         set_category(sig->category());
42         add_scope(sig->scope());
43         add_op(sig->op());
44         if (sig->opnum() != -1) {
45                 add_opnum(sig->opnum());
46         }
47         if (sig->category()!=TT_OBSERVE) {
48                 add_handler_ptype(sig->ptid());
49         }
50         if (sig->otid().len() > 0) {
51                 add_otype(sig->otid());
52                 add_paradigm(TT_OTYPE);
53                 add_paradigm(TT_OBJECT);
54         } else {
55                 add_paradigm(TT_PROCEDURE);
56         }
57         add_reliability(sig->reliability());
58         add_message_class(sig->message_class());
59
60         // We share the arg list from the signature
61         // by just assigning the pointer and letting the ref-counting
62         // system do its thing.  Conceivably, this might cause
63         // consternation if we ever let clients update patterns created
64         // from signatures, since updating the arg list would change
65         // the in-memory copy of the signature and every other pattern
66         // generated from the signature!  However, the most likely
67         // way such updates would be generated would be to send a whole
68         // new pattern over from the client, so sharing the arg list
69         // wouldn\'t cause trouble.
70
71         _args = sig->args();
72
73         // We *cannot* share the context list from the signature as
74         // the signature contains plain _Tt_context entries, while
75         // the pattern should contain _Tt_pat_context entries.
76
77         _Tt_context_list_cursor c(sig->contexts());
78
79         while (c.next()) {
80                 _Tt_pat_context_ptr nc = new _Tt_pat_context(**c);
81                 add_context(nc);
82         }
83 }
84
85
86 _Tt_s_pattern::_Tt_s_pattern ()
87 {
88         _timestamp = 0;
89 }
90
91 _Tt_s_pattern::~_Tt_s_pattern ()
92 {
93 }
94
95
96 // 
97 // Matches a pattern with a message and returns a number indicating how
98 // well the pattern matches the message. 0 indicates no match whereas a
99 // positive number indicates some level of matching. The greater the
100 // level, the "better" the pattern matches the message.  A level of 1
101 // indicates all the pattern fields are wildcards so this is the minimum
102 // level of matching. Any level greater than 1 is controlled by the
103 // number returned from each field match. Thus twiddling these numbers
104 // gives some control as to what field matches are to be considered more
105 // important than others. Presently, the algorithm for selecting match
106 // numbers is that wildcard matches count as 0 whereas any specific match
107 // counts as 1. The total is added up for each field and returned as the
108 // value of the match. The variable tm below holds the total count of
109 // matches. It is initially 1 because wildcard matches don't increment
110 // it. In any submethod called to do matching the variable tm is passed
111 // to the method to be incremented.
112 // 
113 int _Tt_s_pattern::
114 match(const _Tt_s_message &msg, const _Tt_msg_trace &trace) const
115 {
116         int                     tm = 1;
117
118         if (msg.is_handler_copy()) {
119                 // message is the original
120                 if (   (! _generating_ptype.is_null())
121                     && (category() == TT_OBSERVE))
122                 {
123                         //
124                         // A static observer pattern can only match
125                         // the message copy promised to it,
126                         // and cannot match the original.
127                         //
128                         return 0;
129                 }
130         } else {
131                 // message is a copy promised to observer ptype
132                 if (   (_generating_ptype.is_null())
133                     || (   msg.observer()->ptid()
134                         != _generating_ptype->ptid()))
135                 {
136                         //
137                         // If this pattern is not owned by the
138                         // promised ptype, or is owned by no
139                         // ptype, then this pattern cannot
140                         // fulfill the promise.
141                         //
142                         return 0;
143                 }
144         }
145
146         trace << "Tt_message & Tt_pattern {\n";
147         trace.incr_indent();
148         trace << "timestamp:\t" << _timestamp << "\n";
149         trace << *this;
150         trace.decr_indent();
151         trace << "} ";
152
153         // The code for this method is quite repetitive. For each
154         // field, the list is checked to see if there are any values. If
155         // there are and the appropiate match method returns 0 then the
156         // pattern fails to match. The only exception is match_scopes
157         // which defaults to not matching if there are no values specified.
158
159
160         // This function used to not match the op field of the
161         // message, because in the usual code path it is
162         // hashed on the basis of the op field so it would be
163         // redundant to check the op field here.  However, when
164         // matching TT_HANDLER messages just to get callbacks run,
165         // this is not the case.  To save a little time, we skip
166         // the op check if the message paradigm is not TT_HANDLER,
167         // even though that looks really funny here!
168
169         if (0 != _ops->count()) {
170                 if (msg.paradigm() == TT_HANDLER &&
171                     ! match_field(msg.op(), _ops,
172                                   tm, trace, "op" ))
173                 {
174                         return(0);
175                 }
176                 tm++;
177         }
178
179
180
181         if (_reliabilities != 0) {
182                 if (!(_reliabilities&(1<<msg.reliability()))) {
183                         trace << "== 0; /* Tt_disposition */\n";
184                         return(0);
185                 }
186                 tm++;
187         }
188
189         if (_states != 0) {
190                 if (!(_states&(1<<msg.state()))) {
191                         trace << "== 0; /* Tt_state */\n";
192                         return(0);
193                 }
194                 tm++;
195         }
196         if (_classes != 0) {
197                 if (!(_classes&(1<<TT_CLASS_UNDEFINED))
198                     && !(_classes&(1<< msg.message_class()))) {
199                         trace << "== 0; /* Tt_class */\n";
200                         return(0);
201                 }
202                 tm++;
203         }
204         if (_paradigms != 0) {
205                 if (!(_paradigms&(1<<msg.paradigm()))) {
206                         trace << "== 0; /* Tt_address */\n";
207                         return(0);
208                 }
209                 tm++;
210         }
211
212
213         if (_handlers->count()) {
214                 _Tt_string              h;
215
216                 if (! msg.handler().is_null()) {
217                         h = msg.handler()->id();
218                 }
219                 if (! match_field(h, _handlers, tm, trace, "handler")) {
220                         return(0);
221                 }
222         }
223
224         if (_handler_ptypes->count() &&
225             ! match_field(msg.handler_ptype(), _handler_ptypes,
226                           tm, trace, "handler_ptype" ))
227         {
228                 return(0);
229         }
230
231
232         if (! match_scopes(msg, tm, trace)) {
233                 return(0);
234         }
235         if (msg.scope() == TT_SESSION) {
236                 if (_files->count() &&
237                     ! match_field(msg.file(), _files,
238                                   tm, trace, "file"))
239                 {
240                         return(0);
241                 }
242         }
243         if (_objects->count() &&
244             ! match_field(msg.object(), _objects,
245                           tm, trace, "object"))
246         {
247                 return(0);
248         }
249
250         if (_otypes->count() &&
251             ! match_field(msg.otype(), _otypes,
252                           tm, trace, "otype"))
253         {
254                 return(0);
255         }
256         if (_senders->count() &&
257             ! match_field(msg.sender()->id(), _senders,
258                           tm, trace, "sender"))
259         {
260                 return(0);
261         }
262
263         if (_sender_ptypes->count() &&
264             ! match_field(msg.sender_ptype(), _sender_ptypes,
265                           tm, trace, "sender_ptype"))
266         {
267                 return(0);
268         }
269
270
271         if (_args->count() && ! match_args(msg, tm, trace)) {
272                 return(0);
273         }
274
275         if (! match_contexts(msg, tm, trace)) {
276                 return(0);
277         }
278
279         trace << "== " << tm << ";\n";
280         return(tm);
281 }
282
283
284 // 
285 // Generic matching function for lists of integers. If the given pattern
286 // values, pvals, is empty then 0 is returned indicating a mismatch.
287 // Otherwise, 1 is returned if "val" is in the list and the variable tm
288 // is incremented to update the matching score in _Tt_pattern::match.
289 // 
290 int _Tt_s_pattern::
291 match_field(int val, const _Tt_int_rec_list_ptr &pvals, int &tm,
292             const _Tt_msg_trace &trace, const char *failure_note) const
293 {
294         _Tt_int_rec_list_cursor c(pvals);
295
296         while (c.next()) {
297                 if (val == c->val) {
298                         tm++;
299                         return(1);
300                 }
301         }
302         if (failure_note != 0) {
303                 trace <<  "== 0; /* " << failure_note << " */\n";
304         }
305         return(0);
306 }
307
308
309 // 
310 // Generic matching function for lists of strings. If the given pattern
311 // values, pvals, is empty then 0 is returned indicating a mismatch.
312 // Otherwise, 1 is returned if "val" is in the list and the variable tm
313 // is incremented to update the matching score in _Tt_pattern::match.
314 //      
315 int _Tt_s_pattern::
316 match_field(const _Tt_string &val, const _Tt_string_list_ptr &pvals,
317             int &tm, const _Tt_msg_trace &trace,
318             const char *failure_note) const
319 {
320         _Tt_string_list_cursor c(pvals);
321
322         if (val.len() != 0) {
323                 while (c.next()) {
324                         if (val == *c) {
325                                 tm++;
326                                 return(1);
327                         }
328                 }
329         }
330         if (failure_note != 0) {
331                 trace <<  "== 0; /* " << failure_note << " */\n";
332         }
333         return(0);
334 }
335
336
337 // 
338 // Matches the scope of the message with the pattern scopes. For the
339 // different scopes, matching is defined differently as specified below. 
340 // 
341 int _Tt_s_pattern::
342 match_scopes(const _Tt_message &msg, int &tm,
343              const _Tt_msg_trace &trace) const
344 {
345         Tt_scope        s = msg.scope();
346         int             valid_scope_mask = 0;
347         int             rval;
348
349         ASSERT(TT_SCOPE_NONE==0 && TT_SESSION==1 && TT_FILE==2 &&
350                TT_BOTH==3 && TT_FILE_IN_SESSION==4,
351                "Tt_scope enum values changed.  This breaks the following "
352                "code, and also breaks binary compatibility for libtt users. "
353                "You probably don't want to do that, do you?")
354
355         static int valid_scope_masks[] = {
356                 0,              // TT_SCOPE_NONE
357                 (1<<TT_SESSION) | (1<<TT_BOTH), // TT_SESSION
358                 (1<<TT_FILE) | (1<<TT_BOTH),    // TT_FILE
359                 (1<<TT_SESSION) | (1<<TT_FILE) | (1<<TT_BOTH), // TT_BOTH
360                 (1<<TT_FILE_IN_SESSION)};       // TT_FILE_IN_SESSION
361
362         if (! (_scopes & valid_scope_masks[s])) {
363                 trace << "== 0; /* " << _tt_enumname(s) << " != Tt_scopes */\n";
364                 return 0;
365         }
366
367         // if this is not a file-scoped message and the pattern
368         // doesn't contain a value for the _sessions list then we
369         // immediately fail the match.
370         if (s != TT_FILE &&
371             (msg.session().is_null() || _sessions->count() == 0)) {
372                 trace << "== 0; /* pattern not joined to "
373                          "tt_message_session() */\n";
374                 return 0;
375         }
376
377         switch (s) {
378               case TT_FILE:
379                 return match_field(msg.file(), _files,
380                                    tm, trace, "file" );
381               case TT_FILE_IN_SESSION:
382                 if (msg.session()->has_id(_sessions)) {
383                         return((_files->count() == 0)
384                                || match_field(msg.file(), _files,
385                                            tm, trace, "file"));
386                 } else {
387                         trace <<  "== 0; /* file in session */\n";
388                         return(0);
389                 }
390               case TT_SESSION:
391
392                 // The session-scope case is so common that we avoid
393                 // matching the session id by setting a flag on a
394                 // pattern that we know is in the session (see
395                 // _Tt_s_procid::add_pattern and
396                 // _Tt_session::mod_session_id_in_patterns to see how
397                 // the flag is set). This flag is turned on if this
398                 // pattern is joined to the current session or not.
399  
400                 if (in_session()) {
401                         return(1);
402                 } else {
403                         trace << "== 0; /* session */\n";
404                 }
405                 return(0);
406
407               case TT_BOTH:
408                 rval = (msg.session()->has_id(_sessions) +
409                         match_field(msg.file(), _files, tm, trace, 0));
410                 if (rval == 0) {
411                         trace << "== 0; /* file and session */\n";
412                 }
413                 return(rval);
414
415               case TT_SCOPE_NONE:
416               default:
417                 trace << "== 0; /* Tt_scope */\n";
418                 return(0);
419         }
420 }
421
422
423 // 
424 // Returns 1 if the message's args match the pattern args. The
425 // _Tt_arg::is_match method is used to do the bulk of the work.
426 // 
427 int _Tt_s_pattern::
428 match_args(const _Tt_message &msg, int &tm,
429            const _Tt_msg_trace &trace) const
430 {
431         if (msg.args()->count() == 0) {
432                 // arg matching was specified but the
433                 // message doesn't contain any args.
434                 trace << "== 0; /* args */\n";
435                 return(0);
436         }
437
438         _Tt_arg_list_cursor p_args(_args);
439         _Tt_arg_list_cursor m_args(msg.args());
440         int cumulative_args_score = 0;
441
442         while (p_args.next()) {
443
444                 if (!m_args.next()) {
445                         trace << "== 0; /* args */\n";
446                         return(0);
447                 }
448
449                 int used_wildcard;
450                 int score = p_args->match_score(*m_args, used_wildcard);
451                 if (score <= 0) {
452                         trace << "== 0; /* args */\n";
453                         return(0);
454                 }
455                 cumulative_args_score += score;
456         }
457         tm += cumulative_args_score;
458         return(1);
459 }
460
461 //
462 // Returns 1 if the message's contexts match pattern's contexts.
463 //
464 int _Tt_s_pattern::
465 match_contexts(const _Tt_message &msg, int &tm,
466            const _Tt_msg_trace &trace) const
467 {
468         _Tt_pat_context_list_cursor cntxtC( _contexts );
469         while (cntxtC.next()) {
470                 if (((_Tt_s_pat_context &)**cntxtC).matchVal( msg ) == 0) {
471                         trace << "== 0; /* contexts */\n";
472                         return 0;
473                 }
474         }
475         tm++;
476         return 1;
477 }
478
479
480
481 // Routines to set and get the ptype that this pattern was generated
482 // from, and to test if it was so generate.
483
484 _Tt_ptype_ptr &_Tt_s_pattern::
485 generating_ptype(_Tt_ptype_ptr &pt)
486 {
487         _generating_ptype = pt;
488         return pt;
489 }
490
491 _Tt_ptype_ptr &_Tt_s_pattern::
492 generating_ptype()
493 {
494         return _generating_ptype;
495 }
496
497 int _Tt_s_pattern::
498 is_from_ptype()
499 {
500         return !_generating_ptype.is_null();
501 }
502
503 Tt_status _Tt_s_pattern::
504 join_context(const _Tt_msg_context &msgCntxt)
505 {
506         _Tt_pat_context_list_cursor contextC( _contexts );
507         while (contextC.next()) {
508                 if (contextC->slotName() == msgCntxt.slotName()) {
509                         return contextC->addValue( msgCntxt );
510                 }
511         }
512         return TT_WRN_NOTFOUND;
513 }
514
515 Tt_status _Tt_s_pattern::
516 quit_context(const _Tt_msg_context &msgCntxt)
517 {
518         _Tt_pat_context_list_cursor contextC( _contexts );
519         while (contextC.next()) {
520                 if (contextC->slotName() == msgCntxt.slotName()) {
521                         return contextC->deleteValue( msgCntxt );
522                 }
523         }
524         return TT_WRN_NOTFOUND;
525 }