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), _owner_document(NULL), _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(SPDocument *owner_document)
34 : _owner(NULL), _owner_document(owner_document), _obj(NULL), _uri(NULL)
35 {
36 g_assert(_owner_document != NULL);
37 }
39 URIReference::~URIReference() {
40 detach();
41 }
43 void URIReference::attach(const URI &uri) throw(BadURIException)
44 {
45 SPDocument *document;
46 if (_owner) {
47 document = SP_OBJECT_DOCUMENT(_owner);
48 } else if (_owner_document) {
49 document = _owner_document;
50 } else {
51 g_assert_not_reached();
52 }
53 gchar const *fragment = uri.getFragment();
54 if ( !uri.isRelative() || uri.getQuery() || !fragment ) {
55 throw UnsupportedURIException();
56 }
58 /* FIXME !!! real xpointer support should be delegated to document */
59 /* for now this handles the minimal xpointer form that SVG 1.0
60 * requires of us
61 */
62 gchar *id;
63 if (!strncmp(fragment, "xpointer(", 9)) {
64 /* FIXME !!! this is wasteful */
65 /* FIXME: It looks as though this is including "))" in the id. I suggest moving
66 the strlen calculation and validity testing to before strdup, and copying just
67 the id without the "))". -- pjrm */
68 if (!strncmp(fragment, "xpointer(id(", 12)) {
69 id = g_strdup(fragment+12);
70 size_t const len = strlen(id);
71 if ( len < 3 || strcmp(id+len-2, "))") ) {
72 g_free(id);
73 throw MalformedURIException();
74 }
75 } else {
76 throw UnsupportedURIException();
77 }
78 } else {
79 id = g_strdup(fragment);
80 }
82 /* FIXME !!! validate id as an NCName somewhere */
84 if (_uri) {
85 delete _uri;
86 }
87 _uri = new URI(uri);
89 _connection.disconnect();
90 _setObject(document->getObjectById(id));
91 _connection = document->connectIdChanged(id, sigc::mem_fun(*this, &URIReference::_setObject));
93 g_free(id);
94 }
96 void URIReference::detach() {
97 _connection.disconnect();
98 delete _uri;
99 _uri = NULL;
100 _setObject(NULL);
101 }
103 void URIReference::_setObject(SPObject *obj) {
104 if ( obj && !_acceptObject(obj) ) {
105 obj = NULL;
106 }
108 if ( obj == _obj ) return;
110 SPObject *old_obj=_obj;
111 _obj = obj;
113 _release_connection.disconnect();
114 if (_obj) {
115 sp_object_href(_obj, _owner);
116 _release_connection = _obj->connectRelease(sigc::mem_fun(*this, &URIReference::_release));
117 }
118 _changed_signal.emit(old_obj, _obj);
119 if (old_obj) {
120 /* release the old object _after_ the signal emission */
121 sp_object_hunref(old_obj, _owner);
122 }
123 }
125 /* If an object is deleted, current semantics require that we release
126 * it on its "release" signal, rather than later, when its ID is actually
127 * unregistered from the document.
128 */
129 void URIReference::_release(SPObject *obj) {
130 g_assert( _obj == obj );
131 _setObject(NULL);
132 }
134 } /* namespace Inkscape */
136 static gchar *
137 uri_to_id(SPDocument *document, const gchar *uri)
138 {
139 const gchar *e;
140 gchar *id;
141 gint len;
143 g_return_val_if_fail (document != NULL, NULL);
145 if (!uri) return NULL;
146 /* fixme: xpointer, everything */
147 if (strncmp (uri, "url(#", 5)) return NULL;
149 e = uri + 5;
150 while (*e) {
151 if (*e == ')') break;
152 if (!isalnum (*e) && (*e != '_') && (*e != '-') && (*e != ':') && (*e != '.')) return NULL;
153 e += 1;
154 if (!*e) return NULL;
155 }
157 len = e - uri - 5;
158 if (len < 1) return NULL;
160 id = (gchar*)g_new(gchar, len + 1);
161 memcpy (id, uri + 5, len);
162 id[len] = '\0';
164 return id;
165 }
167 SPObject *
168 sp_uri_reference_resolve (SPDocument *document, const gchar *uri)
169 {
170 gchar *id;
172 id = uri_to_id(document, uri);
173 if (!id) return NULL;
175 SPObject *ref;
176 ref = document->getObjectById(id);
177 g_free(id);
178 return ref;
179 }