Remove features from service discovery module when detaching module from stream
fixes #179 fixes #812
This commit is contained in:
parent
3a8fef7b7b
commit
7c4260eed7
|
@ -24,7 +24,7 @@ public class EntityCapabilitiesStorage : Xep.EntityCapabilities.Storage, Object
|
|||
}
|
||||
}
|
||||
|
||||
public void store_identities(string entity, Gee.List<Identity> identities) {
|
||||
public void store_identities(string entity, Gee.Set<Identity> identities) {
|
||||
foreach (Identity identity in identities) {
|
||||
if (identity.category == Identity.CATEGORY_CLIENT) {
|
||||
db.entity_identity.insert()
|
||||
|
|
|
@ -26,6 +26,7 @@ public class Module : XmppStreamModule, Jet.EnvelopEncoding {
|
|||
}
|
||||
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public bool is_available(XmppStream stream, Jid full_jid) {
|
||||
|
|
|
@ -35,7 +35,9 @@ public class StreamModule : XmppStreamModule {
|
|||
stream.get_module(Pubsub.Module.IDENTITY).add_filtered_notification(stream, NODE_DEVICELIST, (stream, jid, id, node) => parse_device_list(stream, jid, id, node), null);
|
||||
}
|
||||
|
||||
public override void detach(XmppStream stream) {}
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(Pubsub.Module.IDENTITY).remove_filtered_notification(stream, NODE_DEVICELIST);
|
||||
}
|
||||
|
||||
public async ArrayList<int32> request_user_devicelist(XmppStream stream, Jid jid) {
|
||||
var future = active_devicelist_requests[jid];
|
||||
|
|
|
@ -6,11 +6,24 @@ public class Flag : XmppStreamFlag {
|
|||
public static FlagIdentity<Flag> IDENTITY = new FlagIdentity<Flag>(NS_URI, "service_discovery");
|
||||
|
||||
private HashMap<Jid, Gee.List<string>?> entity_features = new HashMap<Jid, Gee.List<string>?>(Jid.hash_func, Jid.equals_func);
|
||||
private HashMap<Jid, Gee.List<Identity>?> entity_identities = new HashMap<Jid, Gee.List<Identity>?>(Jid.hash_func, Jid.equals_func);
|
||||
private HashMap<Jid, Gee.Set<Identity>?> entity_identities = new HashMap<Jid, Gee.Set<Identity>?>(Jid.hash_func, Jid.equals_func);
|
||||
private HashMap<Jid, Gee.List<Item>?> entity_items = new HashMap<Jid, Gee.List<Item>?>(Jid.hash_func, Jid.equals_func);
|
||||
public Gee.List<string> features = new ArrayList<string>();
|
||||
|
||||
public Gee.List<Identity>? get_entity_categories(Jid jid) {
|
||||
private Gee.Set<string> own_features_ = new HashSet<string>();
|
||||
public Gee.List<string> own_features {
|
||||
owned get {
|
||||
var ret = new ArrayList<string>();
|
||||
foreach (var feature in own_features_) ret.add(feature);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private Gee.Set<Identity> own_identities_ = new HashSet<Identity>(Identity.hash_func, Identity.equals_func);
|
||||
public Gee.Set<Identity> own_identities {
|
||||
owned get { return own_identities_.read_only_view; }
|
||||
}
|
||||
|
||||
public Gee.Set<Identity>? get_entity_categories(Jid jid) {
|
||||
return entity_identities.has_key(jid) ? entity_identities[jid] : null; // TODO isn’t this default for hashmap
|
||||
}
|
||||
|
||||
|
@ -29,7 +42,7 @@ public class Flag : XmppStreamFlag {
|
|||
return entity_features[jid].contains(feature);
|
||||
}
|
||||
|
||||
public void set_entity_identities(Jid jid, Gee.List<Identity>? identities) {
|
||||
public void set_entity_identities(Jid jid, Gee.Set<Identity>? identities) {
|
||||
entity_identities[jid] = identities;
|
||||
}
|
||||
|
||||
|
@ -41,7 +54,20 @@ public class Flag : XmppStreamFlag {
|
|||
entity_items[jid] = features;
|
||||
}
|
||||
|
||||
public void add_own_feature(string feature) { features.add(feature); }
|
||||
public void add_own_feature(string feature) {
|
||||
if (own_features_.contains(feature)) {
|
||||
warning("Tried to add the feature %s a second time".printf(feature));
|
||||
return;
|
||||
}
|
||||
own_features_.add(feature);
|
||||
}
|
||||
|
||||
public void remove_own_feature(string feature) {
|
||||
own_features_.remove(feature);
|
||||
}
|
||||
|
||||
public void add_own_identity(Identity identity) { own_identities_.add(identity); }
|
||||
public void remove_own_identity(Identity identity) { own_identities_.remove(identity); }
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
|
||||
|
|
|
@ -17,6 +17,18 @@ public class Identity {
|
|||
this.type_ = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static uint hash_func(Identity a) {
|
||||
uint hash = a.category.hash() ^ a.type_.hash();
|
||||
if (a.name != null) {
|
||||
hash ^= a.name.hash();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static bool equals_func(Identity a, Identity b) {
|
||||
return a.category == b.category && a.type_ == b.type_ && a.name == b.name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ public class InfoResult {
|
|||
}
|
||||
}
|
||||
|
||||
public Gee.List<Identity> identities {
|
||||
public Gee.Set<Identity> identities {
|
||||
owned get {
|
||||
ArrayList<Identity> ret = new ArrayList<Identity>();
|
||||
HashSet<Identity> ret = new HashSet<Identity>();
|
||||
foreach (StanzaNode feature_node in iq.stanza.get_subnode("query", NS_URI_INFO).get_subnodes("identity", NS_URI_INFO)) {
|
||||
ret.add(new Identity(feature_node.get_attribute("category", NS_URI_INFO),
|
||||
feature_node.get_attribute("type", NS_URI_INFO),
|
||||
|
|
|
@ -9,27 +9,39 @@ public const string NS_URI_ITEMS = NS_URI + "#items";
|
|||
public class Module : XmppStreamModule, Iq.Handler {
|
||||
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0030_service_discovery_module");
|
||||
|
||||
public ArrayList<Identity> identities = new ArrayList<Identity>();
|
||||
public Identity own_identity;
|
||||
|
||||
public Module.with_identity(string category, string type, string? name = null) {
|
||||
add_identity(category, type, name);
|
||||
own_identity = new Identity(category, type, name);
|
||||
}
|
||||
|
||||
public void add_feature(XmppStream stream, string feature) {
|
||||
stream.get_flag(Flag.IDENTITY).add_own_feature(feature);
|
||||
}
|
||||
|
||||
public void remove_feature(XmppStream stream, string feature) {
|
||||
stream.get_flag(Flag.IDENTITY).remove_own_feature(feature);
|
||||
}
|
||||
|
||||
public void add_feature_notify(XmppStream stream, string feature) {
|
||||
add_feature(stream, feature + "+notify");
|
||||
}
|
||||
|
||||
public void add_identity(string category, string type, string? name = null) {
|
||||
identities.add(new Identity(category, type, name));
|
||||
public void remove_feature_notify(XmppStream stream, string feature) {
|
||||
remove_feature(stream, feature + "+notify");
|
||||
}
|
||||
|
||||
public delegate void HasEntryCategoryRes(XmppStream stream, Gee.List<Identity>? identities);
|
||||
public void add_identity(XmppStream stream, Identity identity) {
|
||||
stream.get_flag(Flag.IDENTITY).add_own_identity(identity);
|
||||
}
|
||||
|
||||
public void remove_identity(XmppStream stream, Identity identity) {
|
||||
stream.get_flag(Flag.IDENTITY).remove_own_identity(identity);
|
||||
}
|
||||
|
||||
public delegate void HasEntryCategoryRes(XmppStream stream, Gee.Set<Identity>? identities);
|
||||
public void get_entity_categories(XmppStream stream, Jid jid, owned HasEntryCategoryRes listener) {
|
||||
Gee.List<Identity>? res = stream.get_flag(Flag.IDENTITY).get_entity_categories(jid);
|
||||
Gee.Set<Identity>? res = stream.get_flag(Flag.IDENTITY).get_entity_categories(jid);
|
||||
if (res != null) listener(stream, res);
|
||||
request_info(stream, jid, (stream, query_result) => {
|
||||
listener(stream, query_result != null ? query_result.identities : null);
|
||||
|
@ -68,12 +80,17 @@ public class Module : XmppStreamModule, Iq.Handler {
|
|||
|
||||
public override void attach(XmppStream stream) {
|
||||
stream.add_flag(new Flag());
|
||||
add_identity(stream, own_identity);
|
||||
|
||||
stream.get_module(Iq.Module.IDENTITY).register_for_namespace(NS_URI_INFO, this);
|
||||
add_feature(stream, NS_URI_INFO);
|
||||
}
|
||||
|
||||
public override void detach(XmppStream stream) {
|
||||
remove_identity(stream, own_identity);
|
||||
|
||||
stream.get_module(Iq.Module.IDENTITY).unregister_from_namespace(NS_URI_INFO, this);
|
||||
remove_feature(stream, NS_URI_INFO);
|
||||
}
|
||||
|
||||
public static void require(XmppStream stream) {
|
||||
|
@ -85,8 +102,8 @@ public class Module : XmppStreamModule, Iq.Handler {
|
|||
|
||||
private void send_query_result(XmppStream stream, Iq.Stanza iq_request) {
|
||||
InfoResult query_result = new ServiceDiscovery.InfoResult(iq_request);
|
||||
query_result.features = stream.get_flag(Flag.IDENTITY).features;
|
||||
query_result.identities = identities;
|
||||
query_result.features = stream.get_flag(Flag.IDENTITY).own_features;
|
||||
query_result.identities = stream.get_flag(Flag.IDENTITY).own_identities;
|
||||
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, query_result.iq);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -221,9 +221,7 @@ public class Module : XmppStreamModule {
|
|||
stream.get_module(Presence.Module.IDENTITY).received_presence.connect(check_for_enter_error);
|
||||
stream.get_module(Presence.Module.IDENTITY).received_available.connect(on_received_available);
|
||||
stream.get_module(Presence.Module.IDENTITY).received_unavailable.connect(on_received_unavailable);
|
||||
if (stream.get_module(ServiceDiscovery.Module.IDENTITY) != null) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
}
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override void detach(XmppStream stream) {
|
||||
|
@ -232,6 +230,7 @@ public class Module : XmppStreamModule {
|
|||
stream.get_module(Presence.Module.IDENTITY).received_presence.disconnect(check_for_enter_error);
|
||||
stream.get_module(Presence.Module.IDENTITY).received_available.disconnect(on_received_available);
|
||||
stream.get_module(Presence.Module.IDENTITY).received_unavailable.disconnect(on_received_unavailable);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
|
|
|
@ -29,6 +29,12 @@ namespace Xmpp.Xep.Pubsub {
|
|||
}
|
||||
}
|
||||
|
||||
public void remove_filtered_notification(XmppStream stream, string node) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature_notify(stream, node);
|
||||
item_listeners.unset(node);
|
||||
retract_listeners.unset(node);
|
||||
}
|
||||
|
||||
public async Gee.List<StanzaNode>? request_all(XmppStream stream, Jid jid, string node) { // TODO multiple nodes gehen auch
|
||||
Iq.Stanza request_iq = new Iq.Stanza.get(new StanzaNode.build("pubsub", NS_URI).add_self_xmlns().put_node(new StanzaNode.build("items", NS_URI).put_attribute("node", node)));
|
||||
request_iq.to = jid;
|
||||
|
|
|
@ -35,7 +35,9 @@ namespace Xmpp.Xep.UserAvatars {
|
|||
stream.get_module(Pubsub.Module.IDENTITY).add_filtered_notification(stream, NS_URI_METADATA, on_pupsub_event, null);
|
||||
}
|
||||
|
||||
public override void detach(XmppStream stream) { }
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(Pubsub.Module.IDENTITY).remove_filtered_notification(stream, NS_URI_METADATA);
|
||||
}
|
||||
|
||||
|
||||
public void on_pupsub_event(XmppStream stream, Jid jid, string id, StanzaNode? node) {
|
||||
|
|
|
@ -37,6 +37,7 @@ public class Module : XmppStreamModule {
|
|||
}
|
||||
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
stream.get_module(MessageModule.IDENTITY).received_message.disconnect(on_received_message);
|
||||
stream.get_module(MessageModule.IDENTITY).send_pipeline.disconnect(send_pipeline_listener);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Xmpp.Xep.EntityCapabilities {
|
|||
|
||||
private string get_own_hash(XmppStream stream) {
|
||||
if (own_ver_hash == null) {
|
||||
own_ver_hash = compute_hash(stream.get_module(ServiceDiscovery.Module.IDENTITY).identities, stream.get_flag(ServiceDiscovery.Flag.IDENTITY).features, new ArrayList<DataForms.DataForm>());
|
||||
own_ver_hash = compute_hash(stream.get_flag(ServiceDiscovery.Flag.IDENTITY).own_identities, stream.get_flag(ServiceDiscovery.Flag.IDENTITY).own_features, new ArrayList<DataForms.DataForm>());
|
||||
}
|
||||
return own_ver_hash;
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ namespace Xmpp.Xep.EntityCapabilities {
|
|||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza);
|
||||
stream.get_module(Presence.Module.IDENTITY).received_presence.disconnect(on_received_presence);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
|
@ -86,7 +87,10 @@ namespace Xmpp.Xep.EntityCapabilities {
|
|||
}
|
||||
}
|
||||
|
||||
private static string compute_hash(Gee.List<ServiceDiscovery.Identity> identities, Gee.List<string> features, Gee.List<DataForms.DataForm> data_forms) {
|
||||
private static string compute_hash(Gee.Set<ServiceDiscovery.Identity> identities_set, Gee.List<string> features, Gee.List<DataForms.DataForm> data_forms) {
|
||||
var identities = new ArrayList<ServiceDiscovery.Identity>();
|
||||
foreach (var identity in identities_set) identities.add(identity);
|
||||
|
||||
identities.sort(compare_identities);
|
||||
features.sort();
|
||||
|
||||
|
@ -154,7 +158,7 @@ namespace Xmpp.Xep.EntityCapabilities {
|
|||
}
|
||||
|
||||
public interface Storage : Object {
|
||||
public abstract void store_identities(string entity, Gee.List<ServiceDiscovery.Identity> identities);
|
||||
public abstract void store_identities(string entity, Gee.Set<ServiceDiscovery.Identity> identities);
|
||||
public abstract void store_features(string entity, Gee.List<string> capabilities);
|
||||
public abstract ServiceDiscovery.Identity? get_identities(string entity);
|
||||
public abstract Gee.List<string> get_features(string entity);
|
||||
|
|
|
@ -127,6 +127,7 @@ public class Module : XmppStreamModule, Iq.Handler {
|
|||
current_stream = stream;
|
||||
}
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
stream.get_module(Iq.Module.IDENTITY).unregister_from_namespace(NS_URI, this);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace Xmpp.Xep.MessageDeliveryReceipts {
|
|||
}
|
||||
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
stream.get_module(MessageModule.IDENTITY).received_message.disconnect(received_message);
|
||||
stream.get_module(MessageModule.IDENTITY).send_pipeline.disconnect(send_pipeline_listener);
|
||||
}
|
||||
|
|
|
@ -72,8 +72,9 @@ public class Module : XmppStreamModule, Iq.Handler {
|
|||
}
|
||||
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.stream_negotiated.disconnect(on_stream_negotiated);
|
||||
stream.get_module(Iq.Module.IDENTITY).unregister_from_namespace(NS_URI, this);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
stream.stream_negotiated.disconnect(on_stream_negotiated);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace Xmpp.Xep.Ping {
|
|||
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(Iq.Module.IDENTITY).unregister_from_namespace(NS_URI, this);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public void on_iq_get(XmppStream stream, Iq.Stanza iq) {
|
||||
|
|
|
@ -13,7 +13,9 @@ public class Module : Jingle.ContentType, XmppStreamModule {
|
|||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
stream.get_module(Jingle.Module.IDENTITY).register_content_type(this);
|
||||
}
|
||||
public override void detach(XmppStream stream) { }
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public string content_type_ns_uri() {
|
||||
return NS_URI;
|
||||
|
|
|
@ -13,7 +13,9 @@ public class Module : Jingle.Transport, XmppStreamModule {
|
|||
stream.get_module(Jingle.Module.IDENTITY).register_transport(this);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
}
|
||||
public override void detach(XmppStream stream) { }
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
public override string get_id() { return IDENTITY.id; }
|
||||
|
|
|
@ -14,7 +14,9 @@ public class Module : Jingle.Transport, XmppStreamModule {
|
|||
stream.get_module(Jingle.Module.IDENTITY).register_transport(this);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
}
|
||||
public override void detach(XmppStream stream) { }
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
public override string get_id() { return IDENTITY.id; }
|
||||
|
|
|
@ -26,6 +26,7 @@ public class Module : XmppStreamModule {
|
|||
public override void detach(XmppStream stream) {
|
||||
stream.stream_negotiated.disconnect(enable);
|
||||
stream.get_module(MessageModule.IDENTITY).received_pipeline.disconnect(received_pipeline_listener);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() {
|
||||
|
|
|
@ -21,7 +21,9 @@ public class Module : XmppStreamModule {
|
|||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override void detach(XmppStream stream) {}
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
|
||||
|
|
|
@ -36,8 +36,9 @@ public class Module : XmppStreamModule {
|
|||
}
|
||||
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(MessageModule.IDENTITY).received_message.disconnect(on_received_message);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
stream.get_module(MessageModule.IDENTITY).send_pipeline.disconnect(send_pipeline_listener);
|
||||
stream.get_module(MessageModule.IDENTITY).received_message.disconnect(on_received_message);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
|
|
|
@ -14,7 +14,9 @@ public class Module : XmppStreamModule {
|
|||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override void detach(XmppStream stream) {}
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ public class Module : XmppStreamModule, SecurityPrecondition {
|
|||
}
|
||||
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public bool is_available(XmppStream stream, Jid full_jid) {
|
||||
|
@ -139,4 +140,4 @@ public interface EnvelopEncoding : Object {
|
|||
public abstract void encode_envelop(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, SecurityParameters security_params, StanzaNode security);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,9 @@ public class Module : BookmarksProvider, XmppStreamModule {
|
|||
stream.get_module(Pubsub.Module.IDENTITY).add_filtered_notification(stream, NS_URI, on_pupsub_item, on_pupsub_retract);
|
||||
}
|
||||
|
||||
public override void detach(XmppStream stream) { }
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(Pubsub.Module.IDENTITY).remove_filtered_notification(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
public override string get_id() { return IDENTITY.id; }
|
||||
|
|
Loading…
Reference in a new issue