Code

moving trunk for module inkscape
[inkscape.git] / src / uri-references.cpp
1 #define __SP_URI_REFERENCES_C__
3 /*
4  * Helper methods for resolving URI References
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *
9  * Copyright (C) 2001-2002 Lauris Kaplinski
10  * Copyright (C) 2001 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #include "document.h"
16 #include "sp-object.h"
17 #include "uri.h"
18 #include "uri-references.h"
20 static gchar *uri_to_id(SPDocument *document, const gchar *uri);
22 namespace Inkscape {
24 URIReference::URIReference(SPObject *owner)
25 : _owner(owner), _obj(NULL), _uri(NULL)
26 {
27         g_assert(_owner != NULL);
28         /* FIXME !!! attach to owner's destroy signal to clean up in case */
29 }
31 URIReference::~URIReference() {
32         detach();
33 }
35 void URIReference::attach(const URI &uri) throw(BadURIException)
36 {
37         SPDocument *document = SP_OBJECT_DOCUMENT(_owner);
38         gchar const *fragment = uri.getFragment();
39         if ( !uri.isRelative() || uri.getQuery() || !fragment ) {
40                 throw UnsupportedURIException();
41         }
43         /* FIXME !!! real xpointer support should be delegated to document */
44         /* for now this handles the minimal xpointer form that SVG 1.0
45          * requires of us
46          */
47         gchar *id;
48         if (!strncmp(fragment, "xpointer(", 9)) {
49                 /* FIXME !!! this is wasteful */
50                 /* FIXME: It looks as though this is including "))" in the id.  I suggest moving
51                    the strlen calculation and validity testing to before strdup, and copying just
52                    the id without the "))".  -- pjrm */
53                 if (!strncmp(fragment, "xpointer(id(", 12)) {
54                         id = g_strdup(fragment+12);
55                         size_t const len = strlen(id);
56                         if ( len < 3 || strcmp(id+len-2, "))") ) {
57                                 g_free(id);
58                                 throw MalformedURIException();
59                         }
60                 } else {
61                         throw UnsupportedURIException();
62                 }
63         } else {
64                 id = g_strdup(fragment);
65         }
67         /* FIXME !!! validate id as an NCName somewhere */
69         if (_uri) {
70                 delete _uri;
71         }
72         _uri = new URI(uri);
74         _connection.disconnect();
75         _setObject(document->getObjectById(id));
76         _connection = document->connectIdChanged(id, sigc::mem_fun(*this, &URIReference::_setObject));
78         g_free(id);
79 }
81 void URIReference::detach() {
82         _connection.disconnect();
83         delete _uri;
84         _uri = NULL;
85         _setObject(NULL);
86 }
88 void URIReference::_setObject(SPObject *obj) {
89         if ( obj && !_acceptObject(obj) ) {
90                 obj = NULL;
91         }
93         if ( obj == _obj ) return;
95         SPObject *old_obj=_obj;
96         _obj = obj;
98         if (_obj) {
99                 sp_object_href(_obj, _owner);
100                 g_signal_connect(G_OBJECT(_obj), "release", G_CALLBACK(&URIReference::_release), reinterpret_cast<gpointer>(this));
101         }
102         _changed_signal.emit(old_obj, _obj);
103         if (old_obj) {
104                 /* release the old object _after_ the signal emission */
105                 g_signal_handlers_disconnect_by_func(G_OBJECT(old_obj), (void *)&URIReference::_release, reinterpret_cast<gpointer>(this));
106                 sp_object_hunref(old_obj, _owner);
107         }
110 /* If an object is deleted, current semantics require that we release
111  * it on its "release" signal, rather than later, when its ID is actually
112  * unregistered from the document.
113  */
114 void URIReference::_release(SPObject *obj, URIReference *reference) {
115         g_assert( reference->_obj == obj );
116         reference->_setObject(NULL);
119 } /* namespace Inkscape */
121 static gchar *
122 uri_to_id(SPDocument *document, const gchar *uri)
124         const gchar *e;
125         gchar *id;
126         gint len;
128         g_return_val_if_fail (document != NULL, NULL);
130         if (!uri) return NULL;
131         /* fixme: xpointer, everything */
132         if (strncmp (uri, "url(#", 5)) return NULL;
134         e = uri + 5;
135         while (*e) {
136                 if (*e == ')') break;
137                 if (!isalnum (*e) && (*e != '_') && (*e != '-') && (*e != ':') && (*e != '.')) return NULL;
138                 e += 1;
139                 if (!*e) return NULL;
140         }
142         len = e - uri - 5;
143         if (len < 1) return NULL;
145         id = (gchar*)g_new(gchar, len + 1);
146         memcpy (id, uri + 5, len);
147         id[len] = '\0';
149         return id;
152 SPObject *
153 sp_uri_reference_resolve (SPDocument *document, const gchar *uri)
155         gchar *id;
157         id = uri_to_id(document, uri);
158         if (!id) return NULL;
160         SPObject *ref;
161         ref = document->getObjectById(id);
162         g_free(id);
163         return ref;