1 /* ide-xml-completion-values.c
2  *
3  * Copyright 2017 Sebastien Lafargue <slafargue@gnome.org>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * SPDX-License-Identifier: GPL-3.0-or-later
19  */
20 
21 #include <dazzle.h>
22 
23 #include "ide-xml-completion-values.h"
24 #include "ide-xml-position.h"
25 
26 typedef struct
27 {
28   IdeXmlRngDefine *define;
29   gchar           *values;
30   gchar           *prefix;
31 
32   guint            is_initial_state : 1;
33 } MatchingState;
34 
35 static GPtrArray * process_matching_state (MatchingState   *state,
36                                            IdeXmlRngDefine *define);
37 
38 static ValueMatchItem *
value_match_item_new(const gchar * value)39 value_match_item_new (const gchar *value)
40 {
41   ValueMatchItem *item;
42 
43   g_assert (!dzl_str_empty0 (value));
44 
45   item = g_slice_new0 (ValueMatchItem);
46   item->name = g_strdup (value);
47 
48   return item;
49 }
50 
51 static void
value_match_item_free(gpointer data)52 value_match_item_free (gpointer data)
53 {
54   ValueMatchItem *item = (ValueMatchItem *)data;
55 
56   g_clear_pointer (&item->name, g_free);
57   g_slice_free (ValueMatchItem, item);
58 }
59 
60 static GPtrArray *
match_values_new(void)61 match_values_new (void)
62 {
63   GPtrArray *ar;
64 
65   ar = g_ptr_array_new_with_free_func ((GDestroyNotify)value_match_item_free);
66   return ar;
67 }
68 
69 static void
match_values_add(GPtrArray * to_values,GPtrArray * from_values)70 match_values_add (GPtrArray *to_values,
71                   GPtrArray *from_values)
72 {
73   ValueMatchItem *to_item;
74   ValueMatchItem *from_item;
75 
76   g_assert (to_values != NULL);
77 
78   if (from_values == NULL)
79     return;
80 
81   for (gint i = 0; i < from_values->len; ++i)
82     {
83       from_item = g_ptr_array_index (from_values, i);
84       to_item = value_match_item_new (from_item->name);
85       g_ptr_array_add (to_values, to_item);
86     }
87 }
88 
89 static MatchingState *
matching_state_new(IdeXmlRngDefine * define,const gchar * values,const gchar * prefix)90 matching_state_new (IdeXmlRngDefine  *define,
91                     const gchar      *values,
92                     const gchar      *prefix)
93 {
94   MatchingState *state;
95 
96   g_assert (define != NULL);
97 
98   state = g_slice_new0 (MatchingState);
99 
100   state->define = define;
101   state->values = g_strdup (values);
102   state->prefix = g_strdup (prefix);
103 
104   state->is_initial_state = FALSE;
105 
106   return state;
107 }
108 
109 static void
matching_state_free(MatchingState * state)110 matching_state_free (MatchingState *state)
111 {
112   g_clear_pointer (&state->values, g_free);
113   g_clear_pointer (&state->prefix, g_free);
114   g_slice_free (MatchingState, state);
115 }
116 
117 static GPtrArray *
process_value(MatchingState * state)118 process_value (MatchingState *state)
119 {
120   GPtrArray *match_values = NULL;
121   ValueMatchItem *item;
122   const gchar *value;
123 
124   g_assert (state->define->type == IDE_XML_RNG_DEFINE_VALUE);
125 
126   value = (gchar *)state->define->name;
127 
128   if (!dzl_str_empty0 (value) &&
129       (dzl_str_empty0 (state->prefix) || g_str_has_prefix (value, state->prefix)))
130     {
131       match_values = match_values_new ();
132       item = value_match_item_new (value);
133       g_ptr_array_add (match_values, item);
134     }
135 
136   return match_values;
137 }
138 
139 static GPtrArray *
process_choice(MatchingState * state)140 process_choice (MatchingState *state)
141 {
142   GPtrArray *match_values = NULL;
143   IdeXmlRngDefine *defines;
144 
145   g_assert (state->define->type == IDE_XML_RNG_DEFINE_CHOICE);
146 
147   if (NULL != (defines = state->define->content))
148     {
149       match_values = match_values_new ();
150       while (defines != NULL)
151         {
152           g_autoptr (GPtrArray) match = NULL;
153 
154           if (NULL != (match = process_matching_state (state, defines)))
155             {
156               /* TODO: use move */
157               match_values_add (match_values, match);
158             }
159 
160           defines = defines->next;
161         }
162     }
163 
164   return match_values;
165 }
166 
167 static GPtrArray *
process_group(MatchingState * state)168 process_group (MatchingState *state)
169 {
170   GPtrArray *match_values = NULL;
171   IdeXmlRngDefine *defines;
172 
173   g_assert (state->define->type == IDE_XML_RNG_DEFINE_GROUP ||
174             state->define->type == IDE_XML_RNG_DEFINE_ATTRIBUTE ||
175             state->define->type == IDE_XML_RNG_DEFINE_ZEROORMORE ||
176             state->define->type == IDE_XML_RNG_DEFINE_ONEORMORE ||
177             state->define->type == IDE_XML_RNG_DEFINE_OPTIONAL);
178 
179   if (NULL != (defines = state->define->content))
180     {
181       while (defines != NULL)
182         {
183           g_autoptr (GPtrArray) match = NULL;
184 
185           match_values = match_values_new ();
186           if (NULL != (match = process_matching_state (state, defines)))
187             {
188               match_values_add (match_values, match);
189             }
190 
191           defines = defines->next;
192         }
193     }
194 
195   return match_values;
196 }
197 
198 static GPtrArray *
process_matching_state(MatchingState * state,IdeXmlRngDefine * define)199 process_matching_state (MatchingState   *state,
200                         IdeXmlRngDefine *define)
201 {
202   IdeXmlRngDefine *old_define;
203   IdeXmlRngDefineType type;
204   GPtrArray *match_values = NULL;
205 
206   g_assert (state != NULL);
207   g_assert (define != NULL);
208 
209   old_define = state->define;
210   state->define = define;
211 
212   if (state->is_initial_state)
213     {
214       state->is_initial_state = FALSE;
215       type = IDE_XML_RNG_DEFINE_GROUP;
216     }
217   else
218     type = define->type;
219 
220   switch (type)
221     {
222     case IDE_XML_RNG_DEFINE_VALUE:
223       match_values = process_value (state);
224       break;
225 
226     case IDE_XML_RNG_DEFINE_ATTRIBUTE:
227     case IDE_XML_RNG_DEFINE_ATTRIBUTES_GROUP:
228     case IDE_XML_RNG_DEFINE_NOOP:
229     case IDE_XML_RNG_DEFINE_NOTALLOWED:
230     case IDE_XML_RNG_DEFINE_TEXT:
231     case IDE_XML_RNG_DEFINE_DATATYPE:
232     case IDE_XML_RNG_DEFINE_EMPTY:
233     case IDE_XML_RNG_DEFINE_ELEMENT:
234     case IDE_XML_RNG_DEFINE_START:
235     case IDE_XML_RNG_DEFINE_PARAM:
236     case IDE_XML_RNG_DEFINE_EXCEPT:
237     case IDE_XML_RNG_DEFINE_LIST:
238       match_values = NULL;
239       break;
240 
241     case IDE_XML_RNG_DEFINE_DEFINE:
242     case IDE_XML_RNG_DEFINE_REF:
243     case IDE_XML_RNG_DEFINE_PARENTREF:
244     case IDE_XML_RNG_DEFINE_EXTERNALREF:
245       match_values = process_matching_state (state, define->content);
246       break;
247 
248     case IDE_XML_RNG_DEFINE_INTERLEAVE:
249     case IDE_XML_RNG_DEFINE_GROUP:
250     case IDE_XML_RNG_DEFINE_ZEROORMORE:
251     case IDE_XML_RNG_DEFINE_ONEORMORE:
252     case IDE_XML_RNG_DEFINE_OPTIONAL:
253       match_values = process_group (state);
254       break;
255 
256     case IDE_XML_RNG_DEFINE_CHOICE:
257       match_values = process_choice (state);
258       break;
259 
260     default:
261       g_assert_not_reached ();
262     }
263 
264   state->define = old_define;
265 
266   return match_values;
267 }
268 
269 static MatchingState *
create_initial_matching_state(IdeXmlRngDefine * define,const gchar * values,const gchar * prefix)270 create_initial_matching_state (IdeXmlRngDefine  *define,
271                                const gchar      *values,
272                                const gchar      *prefix)
273 {
274   MatchingState *state;
275 
276   g_assert (define != NULL);
277 
278   state = matching_state_new (define, values, prefix);
279   state->is_initial_state = TRUE;
280 
281   return state;
282 }
283 
284 /* Return an array of ValueMatchItem */
285 GPtrArray *
ide_xml_completion_values_get_matches(IdeXmlRngDefine * define,const gchar * values,const gchar * prefix)286 ide_xml_completion_values_get_matches (IdeXmlRngDefine *define,
287                                        const gchar     *values,
288                                        const gchar     *prefix)
289 {
290   MatchingState *initial_state;
291   GPtrArray *match_values = NULL;
292 
293   g_return_val_if_fail (define != NULL, NULL);
294 
295   if (define->content != NULL)
296     {
297       initial_state = create_initial_matching_state (define, values, prefix);
298 
299       initial_state->is_initial_state = TRUE;
300       match_values = process_matching_state (initial_state, define);
301       matching_state_free (initial_state);
302     }
303 
304   return match_values;
305 }
306