1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
4  * umloperation.c : refactored from uml.c, class.c to final use StdProps
5  *                  PROP_TYPE_DARRAY, a list where each element is a set
6  *                  of properies described by the same StdPropDesc
7  * Copyright (C) 2005 Hans Breuer
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  */
23 
24 /*
25 
26 classType = dia.get_object_type ("UML - Class")
27 operType  = dia.get_object_type ("UML - Operation")
28 paramType = dia.get_object_type ("UML - Parameter")
29 for c in theClasses :
30 	klass, h1, h2 = classType.create (0,0) # p.x, p.y
31 	for f in theFunctions :
32 		oper, _h1, _h2 = operType.create (0,0)
33 		oper.properties["name"] = f.name
34 		oper.properties["type"] = f.type
35 
36 		for p in f.parameters :
37 			param, _h1, _h2 = paramType.create(0,0)
38 			param.properties["name"] = p.name
39 			param.properties["type"] = p.type
40 
41 			oper.insert(param, -1)
42 		klass.insert(oper, -1)
43 	layer.add_object(klass)
44 
45  */
46 
47 #ifdef HAVE_CONFIG_H
48 #include <config.h>
49 #endif
50 
51 #include <math.h>
52 #include <string.h>
53 
54 #include "uml.h"
55 #include "properties.h"
56 
57 extern PropEnumData _uml_visibilities[];
58 extern PropEnumData _uml_inheritances[];
59 
60 static PropDescription umloperation_props[] = {
61   { "name", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
62   N_("Name"), NULL, NULL },
63   { "type", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
64   N_("Type"), NULL, NULL },
65   { "comment", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
66   N_("Comment"), NULL, NULL },
67   { "stereotype", PROP_TYPE_STRING, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
68   N_("Stereotype"), NULL, NULL },
69   /* visibility: public, protected, private (other languages?) */
70   { "visibility", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
71   N_("Visibility"), NULL, _uml_visibilities },
72   { "inheritance_type", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
73   N_("Inheritance type"), NULL, _uml_inheritances },
74   { "query", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
75   N_("Query (const)"), NULL, NULL },
76   { "class_scope", PROP_TYPE_BOOL, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
77   N_("Class scope (static)"), NULL, NULL },
78   { "parameters", PROP_TYPE_DARRAY, PROP_FLAG_VISIBLE | PROP_FLAG_OPTIONAL,
79   N_("Parameters"), NULL, NULL },
80 
81   PROP_DESC_END
82 };
83 
84 static PropOffset umloperation_offsets[] = {
85   { "name", PROP_TYPE_STRING, offsetof(UMLOperation, name) },
86   { "type", PROP_TYPE_STRING, offsetof(UMLOperation, type) },
87   { "comment", PROP_TYPE_STRING, offsetof(UMLOperation, comment) },
88   { "stereotype", PROP_TYPE_STRING, offsetof(UMLOperation, stereotype) },
89   { "visibility", PROP_TYPE_ENUM, offsetof(UMLOperation, visibility) },
90   { "inheritance_type", PROP_TYPE_ENUM, offsetof(UMLOperation, inheritance_type) },
91   { "query", PROP_TYPE_BOOL, offsetof(UMLOperation, query) },
92   { "class_scope", PROP_TYPE_BOOL, offsetof(UMLOperation, class_scope) },
93   { "parameters", PROP_TYPE_DARRAY, offsetof(UMLOperation, parameters) },
94   { NULL, 0, 0 },
95 };
96 
97 PropDescDArrayExtra umloperation_extra = {
98   { umloperation_props, umloperation_offsets, "umloperation" },
99   (NewRecordFunc)uml_operation_new,
100   (FreeRecordFunc)uml_operation_destroy
101 };
102 
103 UMLOperation *
uml_operation_new(void)104 uml_operation_new(void)
105 {
106   UMLOperation *op;
107   static gint next_id = 1;
108 
109   op = g_new0(UMLOperation, 1);
110   op->internal_id = next_id++;
111   op->name = g_strdup("");
112   op->comment = g_strdup("");
113   op->visibility = UML_PUBLIC;
114   op->inheritance_type = UML_LEAF;
115 
116 #if 0 /* setup elsewhere */
117   op->left_connection = g_new0(ConnectionPoint, 1);
118   op->right_connection = g_new0(ConnectionPoint, 1);
119 #endif
120   return op;
121 }
122 
123 void
uml_operation_copy_into(UMLOperation * srcop,UMLOperation * destop)124 uml_operation_copy_into(UMLOperation *srcop, UMLOperation *destop)
125 {
126   UMLParameter *param;
127   UMLParameter *newparam;
128   GList *list;
129 
130   destop->internal_id = srcop->internal_id;
131 
132   if (destop->name != NULL) {
133     g_free(destop->name);
134   }
135   destop->name = g_strdup(srcop->name);
136 
137   if (destop->type != NULL) {
138     g_free(destop->type);
139   }
140   if (srcop->type != NULL) {
141     destop->type = g_strdup(srcop->type);
142   } else {
143     destop->type = NULL;
144   }
145 
146   if (destop->stereotype != NULL) {
147     g_free(destop->stereotype);
148   }
149   if(srcop->stereotype != NULL) {
150     destop->stereotype = g_strdup(srcop->stereotype);
151   } else {
152     destop->stereotype = NULL;
153   }
154 
155   if (destop->comment != NULL) {
156     g_free(destop->comment);
157   }
158   if (srcop->comment != NULL) {
159     destop->comment = g_strdup(srcop->comment);
160   } else {
161     destop->comment = NULL;
162   }
163 
164   destop->visibility = srcop->visibility;
165   destop->class_scope = srcop->class_scope;
166   destop->inheritance_type = srcop->inheritance_type;
167   destop->query = srcop->query;
168 
169   list = destop->parameters;
170   while (list != NULL) {
171     param = (UMLParameter *)list->data;
172     uml_parameter_destroy(param);
173     list = g_list_next(list);
174   }
175   destop->parameters = NULL;
176   list = srcop->parameters;
177   while (list != NULL) {
178     param = (UMLParameter *)list->data;
179 
180     newparam = g_new0(UMLParameter, 1);
181     newparam->name = g_strdup(param->name);
182     newparam->type = g_strdup(param->type);
183     newparam->comment = g_strdup(param->comment);
184 
185     if (param->value != NULL)
186       newparam->value = g_strdup(param->value);
187     else
188       newparam->value = NULL;
189     newparam->kind = param->kind;
190 
191     destop->parameters = g_list_append(destop->parameters, newparam);
192 
193     list = g_list_next(list);
194   }
195 }
196 
197 UMLOperation *
uml_operation_copy(UMLOperation * op)198 uml_operation_copy(UMLOperation *op)
199 {
200   UMLOperation *newop;
201 
202   newop = g_new0(UMLOperation, 1);
203 
204   uml_operation_copy_into(op, newop);
205 #if 0 /* setup elsewhere */
206   newop->left_connection = g_new0(ConnectionPoint,1);
207   *newop->left_connection = *op->left_connection;
208   newop->left_connection->object = NULL; /* must be setup later */
209 
210   newop->right_connection = g_new0(ConnectionPoint,1);
211   *newop->right_connection = *op->right_connection;
212   newop->right_connection->object = NULL; /* must be setup later */
213 #endif
214   return newop;
215 }
216 
217 void
uml_operation_destroy(UMLOperation * op)218 uml_operation_destroy(UMLOperation *op)
219 {
220   GList *list;
221   UMLParameter *param;
222 
223   g_free(op->name);
224   if (op->type != NULL)
225     g_free(op->type);
226   if (op->stereotype != NULL)
227     g_free(op->stereotype);
228 
229   g_free(op->comment);
230 
231   list = op->parameters;
232   while (list != NULL) {
233     param = (UMLParameter *)list->data;
234     uml_parameter_destroy(param);
235     list = g_list_next(list);
236   }
237   if (op->wrappos) {
238     g_list_free(op->wrappos);
239   }
240 
241 #if 0 /* freed elsewhere */
242   /* These are merely temporary reminders, don't need to unconnect */
243   g_free(op->left_connection);
244   g_free(op->right_connection);
245 #endif
246   g_free(op);
247 }
248 
249 void
uml_operation_write(AttributeNode attr_node,UMLOperation * op)250 uml_operation_write(AttributeNode attr_node, UMLOperation *op)
251 {
252   GList *list;
253   UMLParameter *param;
254   DataNode composite;
255   DataNode composite2;
256   AttributeNode attr_node2;
257 
258   composite = data_add_composite(attr_node, "umloperation");
259 
260   data_add_string(composite_add_attribute(composite, "name"),
261 		  op->name);
262   data_add_string(composite_add_attribute(composite, "stereotype"),
263 		  op->stereotype);
264   data_add_string(composite_add_attribute(composite, "type"),
265 		  op->type);
266   data_add_enum(composite_add_attribute(composite, "visibility"),
267 		op->visibility);
268   data_add_string(composite_add_attribute(composite, "comment"),
269 		  op->comment);
270   /* Backward compatibility */
271   data_add_boolean(composite_add_attribute(composite, "abstract"),
272 		   op->inheritance_type == UML_ABSTRACT);
273   data_add_enum(composite_add_attribute(composite, "inheritance_type"),
274 		op->inheritance_type);
275   data_add_boolean(composite_add_attribute(composite, "query"),
276 		   op->query);
277   data_add_boolean(composite_add_attribute(composite, "class_scope"),
278 		   op->class_scope);
279 
280   attr_node2 = composite_add_attribute(composite, "parameters");
281 
282   list = op->parameters;
283   while (list != NULL) {
284     param = (UMLParameter *) list->data;
285 
286     composite2 = data_add_composite(attr_node2, "umlparameter");
287 
288     data_add_string(composite_add_attribute(composite2, "name"),
289 		    param->name);
290     data_add_string(composite_add_attribute(composite2, "type"),
291 		    param->type);
292     data_add_string(composite_add_attribute(composite2, "value"),
293 		    param->value);
294     data_add_string(composite_add_attribute(composite2, "comment"),
295 		    param->comment);
296     data_add_enum(composite_add_attribute(composite2, "kind"),
297 		  param->kind);
298     list = g_list_next(list);
299   }
300 }
301 
302 extern char visible_char[];
303 
304 char *
uml_get_operation_string(UMLOperation * operation)305 uml_get_operation_string (UMLOperation *operation)
306 {
307   int len;
308   char *str;
309   GList *list;
310   UMLParameter *param;
311 
312   /* Calculate length: */
313   len = 1 + (operation->name ? strlen (operation->name) : 0) + 1;
314   if(operation->stereotype != NULL && operation->stereotype[0] != '\0') {
315     len += 5 + strlen (operation->stereotype);
316   }
317 
318   list = operation->parameters;
319   while (list != NULL) {
320     param = (UMLParameter  *) list->data;
321     list = g_list_next (list);
322 
323     switch(param->kind)
324       {
325       case UML_UNDEF_KIND:
326 	break;
327       case UML_IN:
328 	len += 3;
329 	break;
330       case UML_OUT:
331 	len += 4;
332 	break;
333       case UML_INOUT:
334 	len += 6;
335 	break;
336       }
337     len += (param->name ? strlen (param->name) : 0);
338     if (param->type != NULL) {
339       len += strlen (param->type);
340       if (param->type[0] && param->name[0]) {
341         len += 1;
342       }
343     }
344     if (param->value != NULL && param->value[0] != '\0') {
345       len += 1 + strlen (param->value);
346     }
347 
348     if (list != NULL) {
349       len += 1; /* ',' */
350     }
351   }
352 
353   len += 1; /* ')' */
354   if (operation->type != NULL && operation->type[0]) {
355     len += 2 + strlen (operation->type);
356   }
357   if(operation->query != 0) {
358     len += 6;
359   }
360 
361   /* generate string: */
362   str = g_malloc (sizeof (char) * (len + 1));
363 
364   str[0] = visible_char[(int) operation->visibility];
365   str[1] = 0;
366 
367   if(operation->stereotype != NULL && operation->stereotype[0] != '\0') {
368     strcat(str, UML_STEREOTYPE_START);
369     strcat(str, operation->stereotype);
370     strcat(str, UML_STEREOTYPE_END);
371     strcat(str, " ");
372   }
373 
374   strcat (str, operation->name ? operation->name : "");
375   strcat (str, "(");
376 
377   list = operation->parameters;
378   while (list != NULL) {
379     param = (UMLParameter  *) list->data;
380     list = g_list_next (list);
381 
382     switch(param->kind)
383       {
384       case UML_UNDEF_KIND:
385 	break;
386       case UML_IN:
387 	strcat (str, "in ");
388 	break;
389       case UML_OUT:
390 	strcat (str, "out ");
391 	break;
392       case UML_INOUT:
393 	strcat (str, "inout ");
394 	break;
395       }
396     strcat (str, param->name ? param->name : "");
397 
398     if (param->type != NULL) {
399       if (param->type[0] && param->name[0]) {
400         strcat (str, ":");
401       }
402       strcat (str, param->type);
403     }
404 
405     if (param->value != NULL && param->value[0] != '\0') {
406       strcat (str, "=");
407       strcat (str, param->value);
408     }
409 
410     if (list != NULL) {
411       strcat (str, ",");
412     }
413   }
414   strcat (str, ")");
415 
416   if (operation->type != NULL &&
417       operation->type[0]) {
418     strcat (str, ": ");
419     strcat (str, operation->type);
420   }
421 
422   if (operation->query != 0) {
423     strcat(str, " const");
424   }
425 
426   g_assert (strlen (str) == len);
427 
428   return str;
429 }
430 
431 /*!
432  * The ownership of these connection points is quite complicated. Instead of being part of the UMLOperation as one may expect
433   * at first, they are somewhat in between the DiaObject (see: DiaObject::connections and the concrete user, here UMLClass)
434   * and the UMLOperation.
435   * But with taking undo state mangement into account it gets even worse. Deleted (to be restored connection points) live inside
436   * the UMLClassChange until they get reverted back to the object *or* get free'd by umlclass_change_free()
437   * Since the implementation of attributes/operations being settable via StdProps there are more places to keep this stuff
438   * consitent. So here comes a tolerant helper.
439   *
440   * NOTE: Same function as uml_attribute_ensure_connection_points(), with C++ it would be a template function ;)
441  */
442 void
uml_operation_ensure_connection_points(UMLOperation * op,DiaObject * obj)443 uml_operation_ensure_connection_points (UMLOperation* op, DiaObject* obj)
444 {
445   if (!op->left_connection)
446     op->left_connection = g_new0(ConnectionPoint,1);
447   op->left_connection->object = obj;
448   if (!op->right_connection)
449     op->right_connection = g_new0(ConnectionPoint,1);
450   op->right_connection->object = obj;
451 }
452