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"
19 #include "extract-uri.h"
21 #include <sigc++/functors/mem_fun.h>
23 namespace Inkscape {
25 URIReference::URIReference(SPObject *owner)
26 : _owner(owner), _owner_document(NULL), _obj(NULL), _uri(NULL)
27 {
28 g_assert(_owner != NULL);
29 /* FIXME !!! attach to owner's destroy signal to clean up in case */
30 }
32 URIReference::URIReference(SPDocument *owner_document)
33 : _owner(NULL), _owner_document(owner_document), _obj(NULL), _uri(NULL)
34 {
35 g_assert(_owner_document != NULL);
36 }
38 URIReference::~URIReference() {
39 detach();
40 }
42 void URIReference::attach(const URI &uri) throw(BadURIException)
43 {
44 SPDocument *document;
45 if (_owner) {
46 document = SP_OBJECT_DOCUMENT(_owner);
47 } else if (_owner_document) {
48 document = _owner_document;
49 } else {
50 g_assert_not_reached();
51 }
52 gchar const *fragment = uri.getFragment();
53 if ( !uri.isRelative() || uri.getQuery() || !fragment ) {
54 throw UnsupportedURIException();
55 }
57 /* FIXME !!! real xpointer support should be delegated to document */
58 /* for now this handles the minimal xpointer form that SVG 1.0
59 * requires of us
60 */
61 gchar *id;
62 if (!strncmp(fragment, "xpointer(", 9)) {
63 /* FIXME !!! this is wasteful */
64 /* FIXME: It looks as though this is including "))" in the id. I suggest moving
65 the strlen calculation and validity testing to before strdup, and copying just
66 the id without the "))". -- pjrm */
67 if (!strncmp(fragment, "xpointer(id(", 12)) {
68 id = g_strdup(fragment+12);
69 size_t const len = strlen(id);
70 if ( len < 3 || strcmp(id+len-2, "))") ) {
71 g_free(id);
72 throw MalformedURIException();
73 }
74 } else {
75 throw UnsupportedURIException();
76 }
77 } else {
78 id = g_strdup(fragment);
79 }
81 /* FIXME !!! validate id as an NCName somewhere */
83 if (_uri) {
84 delete _uri;
85 }
86 _uri = new URI(uri);
88 _connection.disconnect();
89 _setObject(document->getObjectById(id));
90 _connection = document->connectIdChanged(id, sigc::mem_fun(*this, &URIReference::_setObject));
92 g_free(id);
93 }
95 void URIReference::detach() {
96 _connection.disconnect();
97 delete _uri;
98 _uri = NULL;
99 _setObject(NULL);
100 }
102 void URIReference::_setObject(SPObject *obj) {
103 if ( obj && !_acceptObject(obj) ) {
104 obj = NULL;
105 }
107 if ( obj == _obj ) return;
109 SPObject *old_obj=_obj;
110 _obj = obj;
112 _release_connection.disconnect();
113 if (_obj) {
114 sp_object_href(_obj, _owner);
115 _release_connection = _obj->connectRelease(sigc::mem_fun(*this, &URIReference::_release));
116 }
117 _changed_signal.emit(old_obj, _obj);
118 if (old_obj) {
119 /* release the old object _after_ the signal emission */
120 sp_object_hunref(old_obj, _owner);
121 }
122 }
124 /* If an object is deleted, current semantics require that we release
125 * it on its "release" signal, rather than later, when its ID is actually
126 * unregistered from the document.
127 */
128 void URIReference::_release(SPObject *obj) {
129 g_assert( _obj == obj );
130 _setObject(NULL);
131 }
133 } /* namespace Inkscape */
135 SPObject* sp_css_uri_reference_resolve( SPDocument *document, const gchar *uri )
136 {
137 SPObject* ref = 0;
139 if ( document && uri && ( strncmp(uri, "url(", 4) == 0 )) {
140 gchar *trimmed = extract_uri( uri );
141 if ( trimmed ) {
142 ref = sp_uri_reference_resolve( document, trimmed );
143 g_free( trimmed );
144 }
145 }
147 return ref;
148 }
150 SPObject *
151 sp_uri_reference_resolve (SPDocument *document, const gchar *uri)
152 {
153 SPObject* ref = 0;
155 if ( uri && (*uri == '#') ) {
156 ref = document->getObjectById( uri + 1 );
157 }
159 return ref;
160 }