예제 #1
0
SERD_API
SerdNode
serd_node_new_uri(const SerdURI* uri, const SerdURI* base, SerdURI* out)
{
	SerdURI abs_uri = *uri;
	if (base) {
		serd_uri_resolve(uri, base, &abs_uri);
	}

	const size_t len = serd_uri_string_length(&abs_uri);
	uint8_t*     buf = (uint8_t*)malloc(len + 1);

	SerdNode node = { buf, len, len, 0, SERD_URI };  // FIXME: UTF-8

	uint8_t*     ptr        = buf;
	const size_t actual_len = serd_uri_serialise(&abs_uri, string_sink, &ptr);

	buf[actual_len] = '\0';
	node.n_bytes    = actual_len;
	node.n_chars    = actual_len;

	if (out) {
		serd_uri_parse(buf, out);  // TODO: cleverly avoid double parse
	}

	return node;
}
예제 #2
0
파일: SocketWriter.cpp 프로젝트: EQ4/lad
SocketWriter::SocketWriter(URIMap&            map,
                           URIs&              uris,
                           const Raul::URI&   uri,
                           SPtr<Raul::Socket> sock)
	: AtomWriter(map, uris, *this)
	, _map(map)
	, _sratom(sratom_new(&map.urid_map_feature()->urid_map))
	, _uri(uri)
	, _socket(sock)
{
	// Use <ingen:/root/> as base URI so e.g. </foo/bar> will be a path
	_base = serd_node_from_string(SERD_URI, (const uint8_t*)"ingen:/root/");

	serd_uri_parse(_base.buf, &_base_uri);

	_env    = serd_env_new(&_base);
	_writer = serd_writer_new(
		SERD_TURTLE,
		(SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED),
		_env,
		&_base_uri,
		socket_sink,
		this);

	sratom_set_sink(_sratom,
	                (const char*)_base.buf,
	                (SerdStatementSink)serd_writer_write_statement,
	                (SerdEndSink)serd_writer_end_anon,
	                _writer);
}
예제 #3
0
SERD_API
SerdNode
serd_node_new_uri_from_string(const uint8_t* str,
                              const SerdURI* base,
                              SerdURI*       out)
{
	if (!str || str[0] == '\0') {
		return serd_node_new_uri(base, NULL, out);  // Empty URI => Base URI
	}
	SerdURI uri;
	serd_uri_parse(str, &uri);
	return serd_node_new_uri(&uri, base, out);  // Resolve/Serialise
}
예제 #4
0
SERD_API
SerdStatus
serd_writer_set_root_uri(SerdWriter*     writer,
                         const SerdNode* uri)
{
	serd_node_free(&writer->root_node);
	if (uri && uri->buf) {
		writer->root_node = serd_node_copy(uri);
		serd_uri_parse(uri->buf, &writer->root_uri);
	} else {
		writer->root_node = SERD_NODE_NULL;
		writer->root_uri  = SERD_URI_NULL;
	}
	return SERD_SUCCESS;
}
예제 #5
0
SocketWriter::SocketWriter(URIMap&            map,
                           URIs&              uris,
                           const Raul::URI&   uri,
                           SPtr<Raul::Socket> sock)
	: AtomWriter(map, uris, *this)
	, _map(map)
	, _sratom(sratom_new(&map.urid_map_feature()->urid_map))
	, _uri(uri)
	, _socket(sock)
{
	// Use <ingen:/> as base URI, so relative URIs are like bundle paths
	_base = serd_node_from_string(SERD_URI, (const uint8_t*)"ingen:/");

	serd_uri_parse(_base.buf, &_base_uri);

	// Set up serialisation environment
	_env = serd_env_new(&_base);
	serd_env_set_prefix_from_strings(_env, USTR("atom"),  USTR("http://lv2plug.in/ns/ext/atom#"));
	serd_env_set_prefix_from_strings(_env, USTR("patch"), USTR("http://lv2plug.in/ns/ext/patch#"));
	serd_env_set_prefix_from_strings(_env, USTR("doap"),  USTR("http://usefulinc.com/ns/doap#"));
	serd_env_set_prefix_from_strings(_env, USTR("ingen"), USTR(INGEN_NS));
	serd_env_set_prefix_from_strings(_env, USTR("lv2"),   USTR("http://lv2plug.in/ns/lv2core#"));
	serd_env_set_prefix_from_strings(_env, USTR("midi"),  USTR("http://lv2plug.in/ns/ext/midi#"));
	serd_env_set_prefix_from_strings(_env, USTR("owl"),   USTR("http://www.w3.org/2002/07/owl#"));
	serd_env_set_prefix_from_strings(_env, USTR("rdf"),   USTR("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
	serd_env_set_prefix_from_strings(_env, USTR("rdfs"),  USTR("http://www.w3.org/2000/01/rdf-schema#"));
	serd_env_set_prefix_from_strings(_env, USTR("xsd"),   USTR("http://www.w3.org/2001/XMLSchema#"));

	// Make a Turtle writer that writes directly to the socket
	_writer = serd_writer_new(
		SERD_TURTLE,
		(SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED),
		_env,
		&_base_uri,
		socket_sink,
		this);

	// Write namespace prefixes to reduce traffic
	serd_env_foreach(_env, write_prefix, this);

	// Configure sratom to write directly to the writer (and thus the socket)
	sratom_set_sink(_sratom,
	                (const char*)_base.buf,
	                (SerdStatementSink)serd_writer_write_statement,
	                (SerdEndSink)serd_writer_end_anon,
	                _writer);
}
예제 #6
0
SERD_API
SerdNode
serd_node_new_file_uri(const uint8_t* path,
                       const uint8_t* hostname,
                       SerdURI*       out,
                       bool           escape)
{
	const size_t path_len     = strlen((const char*)path);
	const size_t hostname_len = hostname ? strlen((const char*)hostname) : 0;
	const bool   evil         = is_windows_path(path);
	size_t       uri_len      = 0;
	uint8_t*     uri          = NULL;

	if (path[0] == '/' || is_windows_path(path)) {
		uri_len = strlen("file://") + hostname_len + evil;
		uri = (uint8_t*)malloc(uri_len + 1);
		snprintf((char*)uri, uri_len + 1, "file://%s%s",
		         hostname ? (const char*)hostname : "",
		         evil ? "/" : "");
	}

	SerdChunk chunk = { uri, uri_len };
	for (size_t i = 0; i < path_len; ++i) {
		if (evil && path[i] == '\\') {
			serd_chunk_sink("/", 1, &chunk);
		} else if (path[i] == '%') {
			serd_chunk_sink("%%", 2, &chunk);
		} else if (!escape || is_uri_path_char(path[i])) {
			serd_chunk_sink(path + i, 1, &chunk);
		} else {
			char escape_str[4] = { '%', 0, 0, 0 };
			snprintf(escape_str + 1, sizeof(escape_str) - 1, "%X", path[i]);
			serd_chunk_sink(escape_str, 3, &chunk);
		}
	}
	serd_chunk_sink_finish(&chunk);

	if (out) {
		serd_uri_parse(chunk.buf, out);
	}

	return serd_node_from_string(SERD_URI, chunk.buf);
}
예제 #7
0
파일: state.c 프로젝트: dmlloyd/Carla
static SerdWriter*
ttl_writer(SerdSink sink, void* stream, const SerdNode* base, SerdEnv** new_env)
{
	SerdURI base_uri = SERD_URI_NULL;
	if (base && base->buf) {
		serd_uri_parse(base->buf, &base_uri);
	}

	SerdEnv* env = serd_env_new(base);
	set_prefixes(env);

	SerdWriter* writer = serd_writer_new(
		SERD_TURTLE,
		(SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED),
		env,
		&base_uri,
		sink,
		stream);

	*new_env = env;
	return writer;
}
예제 #8
0
SRATOM_API
int
sratom_write(Sratom*         sratom,
             LV2_URID_Unmap* unmap,
             uint32_t        flags,
             const SerdNode* subject,
             const SerdNode* predicate,
             uint32_t        type_urid,
             uint32_t        size,
             const void*     body)
{
	const char* const type        = unmap->unmap(unmap->handle, type_urid);
	uint8_t           idbuf[12]   = "b0000000000";
	SerdNode          id          = serd_node_from_string(SERD_BLANK, idbuf);
	uint8_t           nodebuf[12] = "b0000000000";
	SerdNode          node        = serd_node_from_string(SERD_BLANK, nodebuf);
	SerdNode          object      = SERD_NODE_NULL;
	SerdNode          datatype    = SERD_NODE_NULL;
	SerdNode          language    = SERD_NODE_NULL;
	bool              new_node    = false;
	if (type_urid == 0 && size == 0) {
		object = serd_node_from_string(SERD_URI, USTR(NS_RDF "nil"));
	} else if (type_urid == sratom->forge.String) {
		object = serd_node_from_string(SERD_LITERAL, (const uint8_t*)body);
	} else if (type_urid == sratom->forge.Chunk) {
		datatype = serd_node_from_string(SERD_URI, NS_XSD "base64Binary");
		object   = serd_node_new_blob(body, size, true);
		new_node = true;
	} else if (type_urid == sratom->forge.Literal) {
		const LV2_Atom_Literal_Body* lit = (const LV2_Atom_Literal_Body*)body;
		const uint8_t*         str = USTR(lit + 1);
		object = serd_node_from_string(SERD_LITERAL, str);
		if (lit->datatype) {
			datatype = serd_node_from_string(
				SERD_URI, USTR(unmap->unmap(unmap->handle, lit->datatype)));
		} else if (lit->lang) {
			const char*  lang       = unmap->unmap(unmap->handle, lit->lang);
			const char*  prefix     = "http://lexvo.org/id/iso639-3/";
			const size_t prefix_len = strlen(prefix);
			if (lang && !strncmp(lang, prefix, prefix_len)) {
				language = serd_node_from_string(
					SERD_LITERAL, USTR(lang + prefix_len));
			} else {
				fprintf(stderr, "Unknown language URID %d\n", lit->lang);
			}
		}
	} else if (type_urid == sratom->forge.URID) {
		const uint32_t urid = *(const uint32_t*)body;
		const uint8_t* str  = USTR(unmap->unmap(unmap->handle, urid));
		object = serd_node_from_string(SERD_URI, str);
	} else if (type_urid == sratom->forge.Path) {
		const uint8_t* str = USTR(body);
		if (path_is_absolute((const char*)str)) {
			new_node = true;
			object   = serd_node_new_file_uri(str, NULL, NULL, false);
		} else {
			SerdURI base_uri = SERD_URI_NULL;
			if (!sratom->base_uri.buf ||
			    strncmp((const char*)sratom->base_uri.buf, "file://", 7)) {
				fprintf(stderr, "warning: Relative path but base is not a file URI.\n");
				fprintf(stderr, "warning: Writing ambiguous atom:Path literal.\n");
				object   = serd_node_from_string(SERD_LITERAL, str);
				datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path));
			} else {
				if (sratom->base_uri.buf) {
					serd_uri_parse(sratom->base_uri.buf, &base_uri);
				}
				new_node = true;
				SerdNode rel = serd_node_new_file_uri(str, NULL, NULL, false);
				object = serd_node_new_uri_from_node(&rel, &base_uri, NULL);
				serd_node_free(&rel);
			}
		}
	} else if (type_urid == sratom->forge.URI) {
		const uint8_t* str = USTR(body);
		object = serd_node_from_string(SERD_URI, str);
	} else if (type_urid == sratom->forge.Int) {
		new_node = true;
		object   = serd_node_new_integer(*(const int32_t*)body);
		datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers)
		                                 ? NS_XSD "integer" : NS_XSD "int");
	} else if (type_urid == sratom->forge.Long) {
		new_node = true;
		object   = serd_node_new_integer(*(const int64_t*)body);
		datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers)
		                                 ? NS_XSD "integer" : NS_XSD "long");
	} else if (type_urid == sratom->forge.Float) {
		new_node = true;
		object   = serd_node_new_decimal(*(const float*)body, 8);
		datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers)
		                                 ? NS_XSD "decimal" : NS_XSD "float");
	} else if (type_urid == sratom->forge.Double) {
		new_node = true;
		object   = serd_node_new_decimal(*(const double*)body, 16);
		datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers)
		                                 ? NS_XSD "decimal" : NS_XSD "double");
	} else if (type_urid == sratom->forge.Bool) {
		const int32_t val = *(const int32_t*)body;
		datatype = serd_node_from_string(SERD_URI, NS_XSD "boolean");
		object   = serd_node_from_string(SERD_LITERAL,
		                                 USTR(val ? "true" : "false"));
	} else if (type_urid == sratom->midi_MidiEvent) {
		new_node = true;
		datatype = serd_node_from_string(SERD_URI, USTR(LV2_MIDI__MidiEvent));
		uint8_t* str = (uint8_t*)calloc(size * 2 + 1, 1);
		for (uint32_t i = 0; i < size; ++i) {
			snprintf((char*)str + (2 * i), size * 2 + 1, "%02X",
			         (unsigned)(uint8_t)*((const uint8_t*)body + i));
		}
		object = serd_node_from_string(SERD_LITERAL, USTR(str));
	} else if (type_urid == sratom->atom_Event) {
		const LV2_Atom_Event* ev = (const LV2_Atom_Event*)body;
		gensym(&id, 'e', sratom->next_id++);
		start_object(sratom, &flags, subject, predicate, &id, NULL);
		// TODO: beat time
		SerdNode time = serd_node_new_integer(ev->time.frames);
		SerdNode p    = serd_node_from_string(SERD_URI,
		                                      USTR(LV2_ATOM__frameTime));
		datatype = serd_node_from_string(SERD_URI, NS_XSD "decimal");
		sratom->write_statement(sratom->handle, SERD_ANON_CONT, NULL,
		                        &id, &p, &time, &datatype, &language);
		serd_node_free(&time);

		p = serd_node_from_string(SERD_URI, NS_RDF "value");
		sratom_write(sratom, unmap, SERD_ANON_CONT, &id, &p,
		             ev->body.type, ev->body.size, LV2_ATOM_BODY(&ev->body));
		if (sratom->end_anon) {
			sratom->end_anon(sratom->handle, &id);
		}
	} else if (type_urid == sratom->forge.Tuple) {
		gensym(&id, 't', sratom->next_id++);
		start_object(sratom, &flags, subject, predicate, &id, type);
		SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value");
		flags |= SERD_LIST_O_BEGIN;
		LV2_ATOM_TUPLE_BODY_FOREACH(body, size, i) {
			list_append(sratom, unmap, &flags, &id, &p, &node,
			            i->size, i->type, LV2_ATOM_BODY(i));
		}
예제 #9
0
static bool
write_node(SerdWriter*        writer,
           const SerdNode*    node,
           const SerdNode*    datatype,
           const SerdNode*    lang,
           Field              field,
           SerdStatementFlags flags)
{
	SerdChunk uri_prefix;
	SerdChunk uri_suffix;
	bool      has_scheme;
	switch (node->type) {
	case SERD_BLANK:
		if (writer->syntax != SERD_NTRIPLES
		    && ((field == FIELD_SUBJECT && (flags & SERD_ANON_S_BEGIN))
		        || (field == FIELD_OBJECT && (flags & SERD_ANON_O_BEGIN)))) {
			++writer->indent;
			write_sep(writer, SEP_ANON_BEGIN);
		} else if (writer->syntax != SERD_NTRIPLES
		           && (field == FIELD_SUBJECT && (flags & SERD_LIST_S_BEGIN))) {
			assert(writer->list_depth == 0);
			copy_node(&writer->list_subj, node);
			++writer->list_depth;
			++writer->indent;
			write_sep(writer, SEP_LIST_BEGIN);
		} else if (writer->syntax != SERD_NTRIPLES
		           && (field == FIELD_OBJECT && (flags & SERD_LIST_O_BEGIN))) {
			++writer->indent;
			++writer->list_depth;
			write_sep(writer, SEP_LIST_BEGIN);
		} else if (writer->syntax != SERD_NTRIPLES
		           && ((field == FIELD_SUBJECT && (flags & SERD_EMPTY_S))
		               || (field == FIELD_OBJECT && (flags & SERD_EMPTY_O)))) {
			sink("[]", 2, writer);
		} else {
			sink("_:", 2, writer);
			if (writer->bprefix && !strncmp((const char*)node->buf,
			                                (const char*)writer->bprefix,
			                                writer->bprefix_len)) {
				sink(node->buf + writer->bprefix_len,
				     node->n_bytes - writer->bprefix_len,
				     writer);
			} else {
				sink(node->buf, node->n_bytes, writer);
			}
		}
		break;
	case SERD_CURIE:
		switch (writer->syntax) {
		case SERD_NTRIPLES:
			if (serd_env_expand(writer->env, node, &uri_prefix, &uri_suffix)) {
				w_err(writer, SERD_ERR_BAD_CURIE,
				      "undefined namespace prefix `%s'\n", node->buf);
				return false;
			}
			sink("<", 1, writer);
			write_uri(writer, uri_prefix.buf, uri_prefix.len);
			write_uri(writer, uri_suffix.buf, uri_suffix.len);
			sink(">", 1, writer);
			break;
		case SERD_TURTLE:
			write_lname(writer, node->buf, node->n_bytes);
		}
		break;
	case SERD_LITERAL:
		if (writer->syntax == SERD_TURTLE && datatype && datatype->buf) {
			const char* type_uri = (const char*)datatype->buf;
			if (!strncmp(type_uri, NS_XSD, sizeof(NS_XSD) - 1) && (
				    !strcmp(type_uri + sizeof(NS_XSD) - 1, "boolean") ||
				    !strcmp(type_uri + sizeof(NS_XSD) - 1, "integer"))) {
				sink(node->buf, node->n_bytes, writer);
				break;
			} else if (!strcmp(type_uri + sizeof(NS_XSD) - 1, "decimal") &&
			           strchr((const char*)node->buf, '.') &&
			           node->buf[node->n_bytes - 1] != '.') {
				/* xsd:decimal literals without trailing digits, e.g. "5.", can
				   not be written bare in Turtle.  We could add a 0 which is
				   prettier, but changes the text and breaks round tripping.
				*/
				sink(node->buf, node->n_bytes, writer);
				break;
			}
		}
		if (writer->syntax != SERD_NTRIPLES
		    && (node->flags & (SERD_HAS_NEWLINE|SERD_HAS_QUOTE))) {
			sink("\"\"\"", 3, writer);
			write_text(writer, WRITE_LONG_STRING, node->buf, node->n_bytes);
			sink("\"\"\"", 3, writer);
		} else {
			sink("\"", 1, writer);
			write_text(writer, WRITE_STRING, node->buf, node->n_bytes);
			sink("\"", 1, writer);
		}
		if (lang && lang->buf) {
			sink("@", 1, writer);
			sink(lang->buf, lang->n_bytes, writer);
		} else if (datatype && datatype->buf) {
			sink("^^", 2, writer);
			write_node(writer, datatype, NULL, NULL, FIELD_NONE, flags);
		}
		break;
	case SERD_URI:
		has_scheme = serd_uri_string_has_scheme(node->buf);
		if (field == FIELD_PREDICATE && (writer->syntax == SERD_TURTLE)
		    && !strcmp((const char*)node->buf, NS_RDF "type")) {
			sink("a", 1, writer);
			break;
		} else if ((writer->syntax == SERD_TURTLE)
		           && !strcmp((const char*)node->buf, NS_RDF "nil")) {
			sink("()", 2, writer);
			break;
		} else if (has_scheme && (writer->style & SERD_STYLE_CURIED)) {
			SerdNode  prefix;
			SerdChunk suffix;
			if (serd_env_qualify(writer->env, node, &prefix, &suffix)) {
				write_uri(writer, prefix.buf, prefix.n_bytes);
				sink(":", 1, writer);
				write_uri(writer, suffix.buf, suffix.len);
				break;
			}
		}
		sink("<", 1, writer);
		if (writer->style & SERD_STYLE_RESOLVED) {
			SerdURI in_base_uri, uri, abs_uri;
			serd_env_get_base_uri(writer->env, &in_base_uri);
			serd_uri_parse(node->buf, &uri);
			serd_uri_resolve(&uri, &in_base_uri, &abs_uri);
			bool rooted = uri_is_under(&writer->base_uri, &writer->root_uri);
			SerdURI* root = rooted ? &writer->root_uri : & writer->base_uri;
			if (!uri_is_under(&abs_uri, root) ||
			    writer->syntax == SERD_NTRIPLES) {
				serd_uri_serialise(&abs_uri, uri_sink, writer);
			} else {
				serd_uri_serialise_relative(
					&uri, &writer->base_uri, root, uri_sink, writer);
			}
		} else {
			write_uri(writer, node->buf, node->n_bytes);
		}
		sink(">", 1, writer);
	default:
		break;
	}
	writer->last_sep = SEP_NONE;
	return true;
}
예제 #10
0
파일: writer.c 프로젝트: OpenGanesh/oom
static bool
write_node(SerdWriter*        writer,
           const SerdNode*    node,
           const SerdNode*    datatype,
           const SerdNode*    lang,
           Field              field,
           SerdStatementFlags flags)
{
	SerdChunk uri_prefix;
	SerdChunk uri_suffix;
	switch (node->type) {
	case SERD_NOTHING:
		return false;
	case SERD_BLANK:
		if (writer->syntax != SERD_NTRIPLES
		    && ((field == FIELD_SUBJECT && (flags & SERD_ANON_S_BEGIN))
		        || (field == FIELD_OBJECT && (flags & SERD_ANON_O_BEGIN)))) {
			++writer->indent;
			serd_writer_write_delim(writer, '[');
		} else if (writer->syntax != SERD_NTRIPLES
		    && ((field == FIELD_SUBJECT && (flags & SERD_EMPTY_S))
		        || (field == FIELD_OBJECT && (flags & SERD_EMPTY_O)))) {
			writer->sink("[]", 2, writer->stream);
		} else {
			writer->sink("_:", 2, writer->stream);
			if (writer->bprefix && !strncmp((const char*)node->buf,
			                                (const char*)writer->bprefix,
			                                writer->bprefix_len)) {
				writer->sink(node->buf + writer->bprefix_len,
				             node->n_bytes - writer->bprefix_len,
				             writer->stream);
			} else {
				writer->sink(node->buf, node->n_bytes, writer->stream);
			}
		}
		break;
	case SERD_CURIE:
		switch (writer->syntax) {
		case SERD_NTRIPLES:
			if (serd_env_expand(writer->env, node, &uri_prefix, &uri_suffix)) {
				fprintf(stderr, "Undefined namespace prefix `%s'\n", node->buf);
				return false;
			}
			writer->sink("<", 1, writer->stream);
			write_text(writer, WRITE_URI, uri_prefix.buf, uri_prefix.len, '>');
			write_text(writer, WRITE_URI, uri_suffix.buf, uri_suffix.len, '>');
			writer->sink(">", 1, writer->stream);
			break;
		case SERD_TURTLE:
			writer->sink(node->buf, node->n_bytes, writer->stream);
		}
		break;
	case SERD_LITERAL:
		if (writer->syntax == SERD_TURTLE && datatype && datatype->buf) {
			// TODO: compare against NS_XSD prefix once
			if (!strcmp((const char*)datatype->buf,    NS_XSD "boolean")
			    || !strcmp((const char*)datatype->buf, NS_XSD "decimal")
			    || !strcmp((const char*)datatype->buf, NS_XSD "integer")) {
				writer->sink(node->buf, node->n_bytes, writer->stream);
				break;
			}
		}
		if (writer->syntax != SERD_NTRIPLES
		    && ((node->flags & SERD_HAS_NEWLINE)
		        || (node->flags & SERD_HAS_QUOTE))) {
			writer->sink("\"\"\"", 3, writer->stream);
			write_text(writer, WRITE_LONG_STRING,
			           node->buf, node->n_bytes, '\0');
			writer->sink("\"\"\"", 3, writer->stream);
		} else {
			writer->sink("\"", 1, writer->stream);
			write_text(writer, WRITE_STRING, node->buf, node->n_bytes, '"');
			writer->sink("\"", 1, writer->stream);
		}
		if (lang && lang->buf) {
			writer->sink("@", 1, writer->stream);
			writer->sink(lang->buf, lang->n_bytes, writer->stream);
		} else if (datatype && datatype->buf) {
			writer->sink("^^", 2, writer->stream);
			write_node(writer, datatype, NULL, NULL, FIELD_NONE, flags);
		}
		break;
	case SERD_URI:
		if ((writer->syntax == SERD_TURTLE)
		    && !strcmp((const char*)node->buf, NS_RDF "type")) {
			writer->sink("a", 1, writer->stream);
			return true;
		} else if ((writer->style & SERD_STYLE_CURIED)
		           && serd_uri_string_has_scheme(node->buf)) {
			SerdNode  prefix;
			SerdChunk suffix;
			if (serd_env_qualify(writer->env, node, &prefix, &suffix)) {
				write_text(writer, WRITE_URI, prefix.buf, prefix.n_bytes, '>');
				writer->sink(":", 1, writer->stream);
				write_text(writer, WRITE_URI, suffix.buf, suffix.len, '>');
				return true;
			}
		} else if ((writer->style & SERD_STYLE_RESOLVED)
		           && !serd_uri_string_has_scheme(node->buf)) {
			SerdURI uri;
			if (!serd_uri_parse(node->buf, &uri)) {
				SerdURI abs_uri;
				serd_uri_resolve(&uri, &writer->base_uri, &abs_uri);
				writer->sink("<", 1, writer->stream);
				serd_uri_serialise(&abs_uri, writer->sink, writer->stream);
				writer->sink(">", 1, writer->stream);
				return true;
			}
		}
		writer->sink("<", 1, writer->stream);
		write_text(writer, WRITE_URI, node->buf, node->n_bytes, '>');
		writer->sink(">", 1, writer->stream);
		return true;
	}
	return true;
}