1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) Sébastien Granjoux 2009 <seb.sfo@free.fr>
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "anjuta-token-file.h"
22 #include "anjuta-debug.h"
24 #include <glib-object.h>
30 *---------------------------------------------------------------------------*/
32 struct _AnjutaTokenFile
36 GFile
* file
; /* Corresponding GFile */
38 AnjutaToken
*content
; /* Current file content */
40 AnjutaToken
*save
; /* List of memory block used */
42 gboolean dirty
; /* Set when the file has been modified */
45 struct _AnjutaTokenFileClass
47 GObjectClass parent_class
;
50 static GObjectClass
*parent_class
= NULL
;
53 *---------------------------------------------------------------------------*/
56 *---------------------------------------------------------------------------*/
59 anjuta_token_file_find_position (AnjutaTokenFile
*file
, AnjutaToken
*token
)
66 if (token
== NULL
) return NULL
;
68 if (anjuta_token_get_length (token
) == 0)
70 AnjutaToken
*last
= anjuta_token_last (token
);
72 for (; (token
!= NULL
) && (token
!= last
); token
= anjuta_token_next (token
))
74 if (anjuta_token_get_length (token
) != 0) break;
77 if (anjuta_token_get_length (token
) == 0) return NULL
;
80 pos
= anjuta_token_get_string (token
);
81 for (start
= file
->content
; start
!= NULL
; start
= anjuta_token_next (start
))
83 guint len
= anjuta_token_get_length (start
);
87 ptr
= anjuta_token_get_string (start
);
90 if ((pos
>= ptr
) && (pos
< end
)) break;
93 if ((start
!= NULL
) && (ptr
!= pos
))
95 start
= anjuta_token_split (start
, pos
- ptr
);
96 start
= anjuta_token_next (start
);
103 *---------------------------------------------------------------------------*/
106 anjuta_token_file_load (AnjutaTokenFile
*file
, GError
**error
)
111 anjuta_token_file_unload (file
);
113 if (g_file_load_contents (file
->file
, NULL
, &content
, &length
, NULL
, error
))
117 file
->save
= anjuta_token_new_static (ANJUTA_TOKEN_FILE
, NULL
);
118 file
->content
= anjuta_token_new_static (ANJUTA_TOKEN_FILE
, NULL
);
120 token
= anjuta_token_new_string_len (ANJUTA_TOKEN_FILE
, content
, length
);
121 anjuta_token_prepend_child (file
->save
, token
);
123 token
= anjuta_token_new_static (ANJUTA_TOKEN_FILE
, content
);
124 anjuta_token_prepend_child (file
->content
, token
);
128 return file
->content
;
132 anjuta_token_file_unload (AnjutaTokenFile
*file
)
134 if (file
->content
!= NULL
) anjuta_token_free (file
->content
);
135 file
->content
= NULL
;
137 if (file
->save
!= NULL
) anjuta_token_free (file
->save
);
144 anjuta_token_file_save (AnjutaTokenFile
*file
, GError
**error
)
146 GFileOutputStream
*stream
;
151 stream
= g_file_replace (file
->file
, NULL
, FALSE
, G_FILE_CREATE_NONE
, NULL
, &err
);
154 if (g_error_matches (err
, G_IO_ERROR
, G_IO_ERROR_NOT_FOUND
))
156 /* Perhaps parent directory is missing, try to create it */
157 GFile
*parent
= g_file_get_parent (file
->file
);
159 if (g_file_make_directory_with_parents (parent
, NULL
, NULL
))
161 g_object_unref (parent
);
162 g_clear_error (&err
);
163 stream
= g_file_replace (file
->file
, NULL
, FALSE
, G_FILE_CREATE_NONE
, NULL
, error
);
164 if (stream
== NULL
) return FALSE
;
168 g_object_unref (parent
);
169 g_propagate_error (error
, err
);
176 g_propagate_error (error
, err
);
181 for (token
= file
->content
; token
!= NULL
; token
= anjuta_token_next (token
))
183 if (!(anjuta_token_get_flags (token
) & ANJUTA_TOKEN_REMOVED
) && (anjuta_token_get_length (token
)))
185 if (g_output_stream_write (G_OUTPUT_STREAM (stream
), anjuta_token_get_string (token
), anjuta_token_get_length (token
) * sizeof (char), NULL
, error
) < 0)
193 ok
= ok
&& g_output_stream_close (G_OUTPUT_STREAM (stream
), NULL
, NULL
);
194 g_object_unref (stream
);
201 anjuta_token_file_move (AnjutaTokenFile
*file
, GFile
*new_file
)
203 if (file
->file
) g_object_unref (file
->file
);
204 file
->file
= new_file
!= NULL
? g_object_ref (new_file
) : NULL
;
205 file
->dirty
= new_file
!= NULL
;
209 remove_raw_token (AnjutaToken
*token
, gpointer user_data
)
211 AnjutaTokenFile
*file
= (AnjutaTokenFile
*)user_data
;
213 if ((anjuta_token_get_length (token
) > 0))
215 AnjutaToken
*pos
= anjuta_token_file_find_position (file
, token
);
216 guint len
= anjuta_token_get_length (token
);
222 guint flen
= anjuta_token_get_length (pos
);
225 pos
= anjuta_token_split (pos
, len
);
228 pos
= anjuta_token_free (pos
);
236 anjuta_token_file_remove_token (AnjutaTokenFile
*file
, AnjutaToken
*token
)
238 AnjutaToken
*next
= NULL
;
240 if (token
!= NULL
) next
= anjuta_token_foreach_post_order (token
, remove_raw_token
, file
);
247 * anjuta_token_file_update:
248 * @file: a #AnjutaTokenFile derived class object.
249 * @token: Token to update.
251 * Update the file with all changed token starting from @token. The function can
252 * return an error if the token is not in the file.
254 * Return value: %TRUE is the update is done without error.
257 anjuta_token_file_update (AnjutaTokenFile
*file
, AnjutaToken
*token
)
265 /* Find all token needing an update */
267 /* Find following tokens */
268 for (last
= token
; last
!= NULL
; last
= anjuta_token_next (last
))
270 /* Get all tokens in group */
271 last
= anjuta_token_last (last
);
273 gint flags
= anjuta_token_get_flags (last
);
274 if (!(flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))) break;
277 /* Find first modified token */
280 gint flags
= anjuta_token_get_flags (token
);
281 if (flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))
283 /* Check previous token */
284 for (prev
= token
; prev
!= NULL
; prev
= anjuta_token_previous (prev
))
286 gint flags
= anjuta_token_get_flags (prev
);
287 if (!(flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))) break;
298 token
= anjuta_token_next (token
);
301 /* Find previous token */
302 for (prev
= token
; prev
!= NULL
; prev
= anjuta_token_previous (prev
))
304 gint flags
= anjuta_token_get_flags (prev
);
305 if ((anjuta_token_get_length (prev
) != 0) && !(flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))) break;
308 /* Delete removed token and compute length of added token */
310 for (next
= token
; (next
!= NULL
) && (next
!= last
);)
312 gint flags
= anjuta_token_get_flags (next
);
314 if (flags
& ANJUTA_TOKEN_ADDED
)
316 added
+= anjuta_token_get_length (next
);
318 next
= anjuta_token_next (next
);
325 AnjutaToken
*start
= NULL
;
327 value
= g_new (gchar
, added
);
328 add
= anjuta_token_prepend_child (file
->save
, anjuta_token_new_string_len (ANJUTA_TOKEN_NAME
, value
, added
));
330 /* Find token position */
333 start
= anjuta_token_file_find_position (file
, prev
);
334 if (start
!= NULL
) start
= anjuta_token_split (start
, anjuta_token_get_length (prev
));
338 add
= anjuta_token_new_static_len (ANJUTA_TOKEN_NAME
, value
, added
);
341 anjuta_token_prepend_child (file
->content
, add
);
345 anjuta_token_insert_after (start
, add
);
349 for (next
= token
; (next
!= NULL
) && (next
!= last
);)
351 gint flags
= anjuta_token_get_flags (next
);
353 if (flags
& ANJUTA_TOKEN_ADDED
)
355 guint len
= anjuta_token_get_length (next
);
359 memcpy(value
, anjuta_token_get_string (next
), len
);
360 anjuta_token_set_string (next
, value
, len
);
363 anjuta_token_clear_flags (next
, ANJUTA_TOKEN_ADDED
);
365 else if (flags
& ANJUTA_TOKEN_REMOVED
)
367 next
= anjuta_token_file_remove_token (file
, next
);
370 next
= anjuta_token_next (next
);
379 * anjuta_token_file_get_token_position:
380 * @file: #AnjutaTokenFile object
383 * Returns the position of the token in the file. This position is a number
384 * which doesn't correspond to a line number or a character but respect the
385 * order of token in the file.
387 * Returns: The position of the token or 0 if the token is not in the file.
390 anjuta_token_file_get_token_position (AnjutaTokenFile
*file
, AnjutaToken
*token
)
392 AnjutaToken
*content
;
399 string
= anjuta_token_get_string (token
);
400 if (string
!= NULL
) break;
402 /* token is a group or an empty token, looks for group members or
404 token
= anjuta_token_next_after_children (token
);
405 } while (token
!= NULL
);
407 for (content
= file
->content
; content
!= NULL
; content
= anjuta_token_next (content
))
412 start
= anjuta_token_get_string (content
);
413 len
= anjuta_token_get_length (content
);
415 if ((string
>= start
) && ((string
- start
) < len
))
417 pos
+= string
- start
;
426 return content
== NULL
? 0 : pos
;
431 anjuta_token_file_get_token_location (AnjutaTokenFile
*file
, AnjutaTokenFileLocation
*location
, AnjutaToken
*token
)
433 AnjutaTokenFileLocation loc
= {NULL
, 1, 1};
437 anjuta_token_dump (token
);
440 target
= anjuta_token_get_string (token
);
441 if (target
!= NULL
) break;
443 /* token is a group or an empty token, looks for group members or
445 token
= anjuta_token_next_after_children (token
);
446 } while (token
!= NULL
);
448 for (pos
= file
->content
; pos
!= NULL
; pos
= anjuta_token_next (pos
))
450 if (!(anjuta_token_get_flags (pos
) & ANJUTA_TOKEN_REMOVED
) && (anjuta_token_get_length (pos
) > 0))
455 ptr
= anjuta_token_get_string (pos
);
456 end
= ptr
+ anjuta_token_get_length (pos
);
458 for (; ptr
!= end
; ptr
++)
473 if (location
!= NULL
)
475 location
->filename
= file
->file
== NULL
? NULL
: g_file_get_parse_name (file
->file
);
476 location
->line
= loc
.line
;
477 location
->column
= loc
.column
;
490 anjuta_token_file_get_file (AnjutaTokenFile
*file
)
496 anjuta_token_file_get_content (AnjutaTokenFile
*file
)
498 if (file
->content
== NULL
)
500 anjuta_token_file_load (file
, NULL
);
503 return file
->content
;
507 anjuta_token_file_is_dirty (AnjutaTokenFile
*file
)
513 *---------------------------------------------------------------------------*/
515 /* dispose is the first destruction step. It is used to unref object created
516 * with instance_init in order to break reference counting cycles. This
517 * function could be called several times. All function should still work
518 * after this call. It has to called its parents.*/
521 anjuta_token_file_dispose (GObject
*object
)
523 AnjutaTokenFile
*file
= ANJUTA_TOKEN_FILE (object
);
525 anjuta_token_file_unload (file
);
527 if (file
->file
) g_object_unref (file
->file
);
530 G_OBJECT_CLASS (parent_class
)->dispose (object
);
533 /* instance_init is the constructor. All functions should work after this
537 anjuta_token_file_instance_init (AnjutaTokenFile
*file
)
540 file
->content
= NULL
;
544 /* class_init intialize the class itself not the instance */
547 anjuta_token_file_class_init (AnjutaTokenFileClass
* klass
)
549 GObjectClass
*gobject_class
;
551 g_return_if_fail (klass
!= NULL
);
553 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
554 gobject_class
= G_OBJECT_CLASS (klass
);
556 gobject_class
->dispose
= anjuta_token_file_dispose
;
560 anjuta_token_file_get_type (void)
562 static GType type
= 0;
566 static const GTypeInfo type_info
=
568 sizeof (AnjutaTokenFileClass
),
569 (GBaseInitFunc
) NULL
,
570 (GBaseFinalizeFunc
) NULL
,
571 (GClassInitFunc
) anjuta_token_file_class_init
,
572 (GClassFinalizeFunc
) NULL
,
573 NULL
, /* class_data */
574 sizeof (AnjutaTokenFile
),
576 (GInstanceInitFunc
) anjuta_token_file_instance_init
,
577 NULL
/* value_table */
580 type
= g_type_register_static (G_TYPE_OBJECT
,
581 "AnjutaTokenFile", &type_info
, 0);
588 /* Constructor & Destructor
589 *---------------------------------------------------------------------------*/
592 anjuta_token_file_new (GFile
*gfile
)
594 AnjutaTokenFile
*file
= g_object_new (ANJUTA_TOKEN_FILE_TYPE
, NULL
);
596 if (gfile
) file
->file
= g_object_ref (gfile
);
597 file
->dirty
= gfile
!= NULL
;
603 anjuta_token_file_free (AnjutaTokenFile
*tfile
)
605 g_object_unref (tfile
);