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 #include <sigc++/functors/mem_fun.h>
22 static gchar *uri_to_id(SPDocument *document, const gchar *uri);
24 namespace Inkscape {
26 URIReference::URIReference(SPObject *owner)
27 : _owner(owner), _obj(NULL), _uri(NULL)
28 {
29 g_assert(_owner != NULL);
30 /* FIXME !!! attach to owner's destroy signal to clean up in case */
31 }
33 URIReference::~URIReference() {
34 detach();
35 }
37 void URIReference::attach(const URI &uri) throw(BadURIException)
38 {
39 SPDocument *document = SP_OBJECT_DOCUMENT(_owner);
40 gchar const *fragment = uri.getFragment();
41 if ( !uri.isRelative() || uri.getQuery() || !fragment ) {
42 throw UnsupportedURIException();
43 }
45 /* FIXME !!! real xpointer support should be delegated to document */
46 /* for now this handles the minimal xpointer form that SVG 1.0
47 * requires of us
48 */
49 gchar *id;
50 if (!strncmp(fragment, "xpointer(", 9)) {
51 /* FIXME !!! this is wasteful */
52 /* FIXME: It looks as though this is including "))" in the id. I suggest moving
53 the strlen calculation and validity testing to before strdup, and copying just
54 the id without the "))". -- pjrm */
55 if (!strncmp(fragment, "xpointer(id(", 12)) {
56 id = g_strdup(fragment+12);
57 size_t const len = strlen(id);
58 if ( len < 3 || strcmp(id+len-2, "))") ) {
59 g_free(id);
60 throw MalformedURIException();
61 }
62 } else {
63 throw UnsupportedURIException();
64 }
65 } else {
66 id = g_strdup(fragment);
67 }
69 /* FIXME !!! validate id as an NCName somewhere */
71 if (_uri) {
72 delete _uri;
73 }
74 _uri = new URI(uri);
76 _connection.disconnect();
77 _setObject(document->getObjectById(id));
78 _connection = document->connectIdChanged(id, sigc::mem_fun(*this, &URIReference::_setObject));
80 g_free(id);
81 }
83 void URIReference::detach() {
84 _connection.disconnect();
85 delete _uri;
86 _uri = NULL;
87 _setObject(NULL);
88 }
90 void URIReference::_setObject(SPObject *obj) {
91 if ( obj && !_acceptObject(obj) ) {
92 obj = NULL;
93 }
95 if ( obj == _obj ) return;
97 SPObject *old_obj=_obj;
98 _obj = obj;
100 _release_connection.disconnect();
101 if (_obj) {
102 sp_object_href(_obj, _owner);
103 _release_connection = _obj->connectRelease(sigc::mem_fun(*this, &URIReference::_release));
104 }
105 _changed_signal.emit(old_obj, _obj);
106 if (old_obj) {
107 /* release the old object _after_ the signal emission */
108 sp_object_hunref(old_obj, _owner);
109 }
110 }
112 /* If an object is deleted, current semantics require that we release
113 * it on its "release" signal, rather than later, when its ID is actually
114 * unregistered from the document.
115 */
116 void URIReference::_release(SPObject *obj) {
117 g_assert( _obj == obj );
118 _setObject(NULL);
119 }
121 } /* namespace Inkscape */
123 static gchar *
124 uri_to_id(SPDocument *document, const gchar *uri)
125 {
126 const gchar *e;
127 gchar *id;
128 gint len;
130 g_return_val_if_fail (document != NULL, NULL);
132 if (!uri) return NULL;
133 /* fixme: xpointer, everything */
134 if (strncmp (uri, "url(#", 5)) return NULL;
136 e = uri + 5;
137 while (*e) {
138 if (*e == ')') break;
139 if (!isalnum (*e) && (*e != '_') && (*e != '-') && (*e != ':') && (*e != '.')) return NULL;
140 e += 1;
141 if (!*e) return NULL;
142 }
144 len = e - uri - 5;
145 if (len < 1) return NULL;
147 id = (gchar*)g_new(gchar, len + 1);
148 memcpy (id, uri + 5, len);
149 id[len] = '\0';
151 return id;
152 }
154 SPObject *
155 sp_uri_reference_resolve (SPDocument *document, const gchar *uri)
156 {
157 gchar *id;
159 id = uri_to_id(document, uri);
160 if (!id) return NULL;
162 SPObject *ref;
163 ref = document->getObjectById(id);
164 g_free(id);
165 return ref;
166 }