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
);
242 next
= anjuta_token_free (token
);
249 * anjuta_token_file_update:
250 * @file: a #AnjutaTokenFile derived class object.
251 * @token: Token to update.
253 * Update the file with all changed token starting from @token. The function can
254 * return an error if the token is not in the file.
256 * Return value: TRUE is the update is done without error.
259 anjuta_token_file_update (AnjutaTokenFile
*file
, AnjutaToken
*token
)
267 /* Find all token needing an update */
269 /* Find following tokens */
270 for (last
= token
; last
!= NULL
; last
= anjuta_token_next (last
))
272 /* Get all tokens in group */
273 last
= anjuta_token_last (last
);
275 gint flags
= anjuta_token_get_flags (last
);
276 if (!(flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))) break;
279 /* Find first modified token */
282 gint flags
= anjuta_token_get_flags (token
);
283 if (flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))
285 /* Check previous token */
286 for (prev
= token
; prev
!= NULL
; prev
= anjuta_token_previous (prev
))
288 gint flags
= anjuta_token_get_flags (prev
);
289 if (!(flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))) break;
300 token
= anjuta_token_next (token
);
303 /* Find previous token */
304 for (prev
= token
; prev
!= NULL
; prev
= anjuta_token_previous (prev
))
306 gint flags
= anjuta_token_get_flags (prev
);
307 if ((anjuta_token_get_length (prev
) != 0) && !(flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))) break;
310 /* Delete removed token and compute length of added token */
312 for (next
= token
; (next
!= NULL
) && (next
!= last
);)
314 gint flags
= anjuta_token_get_flags (next
);
316 if (flags
& ANJUTA_TOKEN_ADDED
)
318 added
+= anjuta_token_get_length (next
);
320 next
= anjuta_token_next (next
);
327 AnjutaToken
*start
= NULL
;
329 value
= g_new (gchar
, added
);
330 add
= anjuta_token_prepend_child (file
->save
, anjuta_token_new_string_len (ANJUTA_TOKEN_NAME
, value
, added
));
332 /* Find token position */
335 start
= anjuta_token_file_find_position (file
, prev
);
336 if (start
!= NULL
) start
= anjuta_token_split (start
, anjuta_token_get_length (prev
));
340 add
= anjuta_token_new_static_len (ANJUTA_TOKEN_NAME
, value
, added
);
343 anjuta_token_prepend_child (file
->content
, add
);
347 anjuta_token_insert_after (start
, add
);
351 for (next
= token
; (next
!= NULL
) && (next
!= last
);)
353 gint flags
= anjuta_token_get_flags (next
);
355 if (flags
& ANJUTA_TOKEN_ADDED
)
357 guint len
= anjuta_token_get_length (next
);
361 memcpy(value
, anjuta_token_get_string (next
), len
);
362 anjuta_token_set_string (next
, value
, len
);
365 anjuta_token_clear_flags (next
, ANJUTA_TOKEN_ADDED
);
367 else if (flags
& ANJUTA_TOKEN_REMOVED
)
369 next
= anjuta_token_file_remove_token (file
, next
);
372 next
= anjuta_token_next (next
);
381 * anjuta_token_file_get_token_position:
382 * @file: #AnjutaTokenFile object
385 * Returns the position of the token in the file. This position is a number
386 * which doesn't correspond to a line number or a character but respect the
387 * order of token in the file.
389 * Returns: The position of the token or 0 if the token is not in the file.
392 anjuta_token_file_get_token_position (AnjutaTokenFile
*file
, AnjutaToken
*token
)
394 AnjutaToken
*content
;
401 string
= anjuta_token_get_string (token
);
402 if (string
!= NULL
) break;
404 /* token is a group or an empty token, looks for group members or
406 token
= anjuta_token_next_after_children (token
);
407 } while (token
!= NULL
);
409 for (content
= file
->content
; content
!= NULL
; content
= anjuta_token_next (content
))
414 start
= anjuta_token_get_string (content
);
415 len
= anjuta_token_get_length (content
);
417 if ((string
>= start
) && ((string
- start
) < len
))
419 pos
+= string
- start
;
428 return content
== NULL
? 0 : pos
;
433 anjuta_token_file_get_token_location (AnjutaTokenFile
*file
, AnjutaTokenFileLocation
*location
, AnjutaToken
*token
)
435 AnjutaTokenFileLocation loc
= {NULL
, 1, 1};
439 anjuta_token_dump (token
);
442 target
= anjuta_token_get_string (token
);
443 if (target
!= NULL
) break;
445 /* token is a group or an empty token, looks for group members or
447 token
= anjuta_token_next_after_children (token
);
448 } while (token
!= NULL
);
450 for (pos
= file
->content
; pos
!= NULL
; pos
= anjuta_token_next (pos
))
452 if (!(anjuta_token_get_flags (pos
) & ANJUTA_TOKEN_REMOVED
) && (anjuta_token_get_length (pos
) > 0))
457 ptr
= anjuta_token_get_string (pos
);
458 end
= ptr
+ anjuta_token_get_length (pos
);
460 for (; ptr
!= end
; ptr
++)
475 if (location
!= NULL
)
477 location
->filename
= file
->file
== NULL
? NULL
: g_file_get_parse_name (file
->file
);
478 location
->line
= loc
.line
;
479 location
->column
= loc
.column
;
492 anjuta_token_file_get_file (AnjutaTokenFile
*file
)
498 anjuta_token_file_get_content (AnjutaTokenFile
*file
)
500 if (file
->content
== NULL
)
502 anjuta_token_file_load (file
, NULL
);
505 return file
->content
;
509 anjuta_token_file_is_dirty (AnjutaTokenFile
*file
)
515 *---------------------------------------------------------------------------*/
517 /* dispose is the first destruction step. It is used to unref object created
518 * with instance_init in order to break reference counting cycles. This
519 * function could be called several times. All function should still work
520 * after this call. It has to called its parents.*/
523 anjuta_token_file_dispose (GObject
*object
)
525 AnjutaTokenFile
*file
= ANJUTA_TOKEN_FILE (object
);
527 anjuta_token_file_unload (file
);
529 if (file
->file
) g_object_unref (file
->file
);
532 G_OBJECT_CLASS (parent_class
)->dispose (object
);
535 /* instance_init is the constructor. All functions should work after this
539 anjuta_token_file_instance_init (AnjutaTokenFile
*file
)
542 file
->content
= NULL
;
546 /* class_init intialize the class itself not the instance */
549 anjuta_token_file_class_init (AnjutaTokenFileClass
* klass
)
551 GObjectClass
*gobject_class
;
553 g_return_if_fail (klass
!= NULL
);
555 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
556 gobject_class
= G_OBJECT_CLASS (klass
);
558 gobject_class
->dispose
= anjuta_token_file_dispose
;
562 anjuta_token_file_get_type (void)
564 static GType type
= 0;
568 static const GTypeInfo type_info
=
570 sizeof (AnjutaTokenFileClass
),
571 (GBaseInitFunc
) NULL
,
572 (GBaseFinalizeFunc
) NULL
,
573 (GClassInitFunc
) anjuta_token_file_class_init
,
574 (GClassFinalizeFunc
) NULL
,
575 NULL
, /* class_data */
576 sizeof (AnjutaTokenFile
),
578 (GInstanceInitFunc
) anjuta_token_file_instance_init
,
579 NULL
/* value_table */
582 type
= g_type_register_static (G_TYPE_OBJECT
,
583 "AnjutaTokenFile", &type_info
, 0);
590 /* Constructor & Destructor
591 *---------------------------------------------------------------------------*/
594 anjuta_token_file_new (GFile
*gfile
)
596 AnjutaTokenFile
*file
= g_object_new (ANJUTA_TOKEN_FILE_TYPE
, NULL
);
598 if (gfile
) file
->file
= g_object_ref (gfile
);
599 file
->dirty
= gfile
!= NULL
;
605 anjuta_token_file_free (AnjutaTokenFile
*tfile
)
607 g_object_unref (tfile
);