blob: 69fa76467968acd2ce8f7a71f17637306dcc4347 [file] [log] [blame]
using oneM2MClient.Client;
using oneM2MClient.Resources;
using oneM2MClient.Protocols;
using BaSys40.Utils.ResultHandling;
using BaSys40.Utils.ModelHandling;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace oneM2MClient.Utils
{
public static class Helper
{
public static Uri ConcatUrl(string Scheme, string Host, int? Port, params string[] path)
{
if (!string.IsNullOrEmpty(Host))
{
StringBuilder uriBuilder = new StringBuilder();
if (!string.IsNullOrEmpty(Scheme))
uriBuilder.Append(Scheme).Append("://");
uriBuilder.Append(Host);
if (Port.HasValue)
uriBuilder.Append(":").Append(Port.Value);
if (path != null)
{
string joinedPath = string.Join("/", path);
uriBuilder.Append("/").Append(joinedPath);
}
return new Uri(uriBuilder.ToString());
}
return null;
}
/// <summary>
/// Returns the urils of the resource and its substructure to be discovered
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="labels"></param>
/// <param name="additionalPath"></param>
/// <returns>List with urils of the resource and its substructure</returns>
public static Result<Response> DiscoverResource(IClient client, Request request, out List<string> resourceLinks, params string[] additionalPath)
{
if (additionalPath != null && additionalPath.Length > 0)
request.AddPath(additionalPath);
var contResp = CSEBase.Discover(client, request, oneM2M.FilterUsage.DISCOVERY_CRITERIA, null);
if (contResp.Success && contResp.Entity != null)
{
if (contResp.Entity.Uril.Count > 0)
resourceLinks = contResp.Entity.Uril;
else if (contResp.Entity.PrimitiveContent?.Items?.Count > 0)
{
resourceLinks = new List<string>();
foreach (var item in contResp.Entity.PrimitiveContent.Items)
{
resourceLinks.Add((item as resource).Ri);
}
if (resourceLinks.Count == 0)
resourceLinks = null;
}
else
resourceLinks = null;
return contResp;
}
resourceLinks = null;
return contResp;
}
#region Read-Functions (Always return real resources)
/// <summary>
/// Reads resource by path
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="path"></param>
/// <returns></returns>
public static resource ReadResourceByPath(IClient client, Request request, params string[] path)
{
if (path != null && path.Length > 0)
request.SetPath(path);
return ReadResource(client, request);
}
/// <summary>
/// Reads resource by resource id
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="resourceId"></param>
/// <returns></returns>
public static resource ReadResourceById(IClient client, Request request, string resourceId)
{
if (string.IsNullOrEmpty(resourceId))
return null;
if(resourceId.Contains("/"))
{
resourceId = resourceId.Split('/')[2];
}
request.SetResourcePath(resourceId);
return ReadResource(client, request);
}
public static resource ReadResource(IClient client, Request request)
{
Result<Response> contResp = CSEBase.Retrieve(client, request);
if (contResp.Success && contResp.Entity != null)
{
if (contResp.Entity.TryGetResource(out resource res))
return res;
}
return null;
}
/// <summary>
/// Reads resources in oneM2M tree based on labels
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="labels"></param>
/// <returns>List of resources</returns>
public static Result<Response> ReadResourcesByLabels(IClient client, Request request, List<string> labels, out List<resource> resources)
{
var contResp = CSEBase.Discover(client, request, oneM2M.FilterUsage.DISCOVERY_CRITERIA, null, labels);
if (contResp.Success && contResp.Entity != null)
{
resources = new List<resource>();
if (contResp.Entity.Uril.Count > 0)
foreach (var uril in contResp.Entity.Uril)
{
var resource = ReadResourceByPath(client, request, uril);
if (resource != null)
resources.Add(resource);
}
if (contResp.Entity.PrimitiveContent?.Items.Count > 0)
foreach (var item in contResp.Entity.PrimitiveContent.Items)
{
if (item != null)
resources.Add((resource)item);
}
return contResp;
}
resources = null;
return contResp;
}
public static Result<Response> ReadLatestAddedResource(IClient client, Request request, out resource resource, params string[] additionalPath)
{
if (additionalPath != null && additionalPath.Length > 0)
request.AddPath(additionalPath);
if(!request.RequestPath.Segments.Contains("la"))
request.AddPath("la");
var contResp = CSEBase.Retrieve(client, request);
if (contResp.Success && contResp.Entity != null && contResp.Entity.PrimitiveContent != null)
{
var items = contResp.Entity.PrimitiveContent.Items;
var result = items?.FirstOrDefault();
if (result != null)
resource = result as resource;
else
resource = null;
}
else
resource = null;
return contResp;
}
#endregion
#region Tree-Functions (Always return an ObjectTree)
/// <summary>
/// Returns an object tree with fully requested resources by resource id
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="resourceId"></param>
/// <returns></returns>
public static Result<Response> ReadResourceTreeById(IClient client, Request request, string resourceId, out ObjectTreeBuilder tree)
{
request.SetResourcePath(resourceId);
return ReadResourceTree(client, request, out tree, null);
}
/// <summary>
/// Return an object tree with the full requested resources
/// </summary>
/// <param name="client"></param>
/// <param name="request"></param>
/// <param name="additionalPath"></param>
/// <returns>Object tree with full resources</returns>
public static Result<Response> ReadResourceTree(IClient client, Request request, out ObjectTreeBuilder tree, params string[] additionalPath)
{
if (additionalPath != null && additionalPath.Length > 0)
request.AddPath(additionalPath);
var result = DiscoverResource(client, request, out List<string> resourceReferences, additionalPath);
if (result.Success && resourceReferences != null)
{
List<resource> resources = new List<resource>();
foreach (var resourceReference in resourceReferences)
{
resource res = null;
if (IsPath(resourceReference))
res = ReadResourceByPath(client, request, resourceReference);
else if (IsId(resourceReference))
res = ReadResourceById(client, request, resourceReference);
if (res != null)
resources.Add(res);
}
if (resources.Count > 0)
tree = GetResourceObjectTree(resources);
else
tree = null;
return result;
}
tree = null;
return result;
}
public static void ListObjectInTree(IClient client, Request request, string objName, object obj, params string[] path)
{
if (path != null && path.Length > 0)
request.SetPath(path);
Container.Create(client, request, objName);
var newPath = path.ToList();
newPath.Add(objName);
request.ClearRequest().SetPath(newPath.ToArray());
foreach (PropertyInfo property in obj.GetType().GetProperties())
{
string name = property.Name;
object value = property.GetValue(obj, null);
string content = value == null ? "NULL" : value.ToString();
ContentInstance.Create(client, request, name, content);
request.ClearRequest();
}
}
/// <summary>
/// Returns a resource object tree structure based on a flat list of resources as input. This is done by comparing ParentId with ResourceId.
/// </summary>
/// <param name="items">A flat list of resources</param>
/// <returns>Object tree with resources</returns>
public static ObjectTreeBuilder GetResourceObjectTree(List<resource> items)
{
if (items != null && items.Count > 0)
{
resource firstItem = items[0];
ObjectTreeBuilder root = new ObjectTreeBuilder(firstItem.Rn, firstItem);
//ToDo: Remove cse names
var childrenOfRoot = items.FindAll(c => c.Pi.Replace("/","").Replace("InCSE1", "").Replace("in-cse", "") == firstItem.Ri.Replace("/","").Replace("InCSE1", "").Replace("in-cse",""));
foreach (var child in childrenOfRoot)
{
int index = items.IndexOf(child);
ObjectTreeBuilder treeChild = GetResourceObjectTree(items.GetRange(index, items.Count - index));
root.AddChild(treeChild);
}
return root;
}
else
return null;
}
/// <summary>
/// Writes the entire tree as container structure in the oneM2M hierarchy tree based on the name of the input tree elements
/// </summary>
/// <param name="client">oneM2M-Client</param>
/// <param name="request">Request-Object</param>
/// <param name="tree">Arbitrary tree with named elements</param>
/// <param name="path">Start path in the oneM2M hierarchy where the container structure is added to</param>
public static void WriteTree(IClient client, Request request, ObjectTreeBuilder tree, params string[] path)
{
if (tree != null)
{
if (path != null && path.Length > 0)
request.SetPath(path);
Container.Create(client, request, tree.Name);
request.ClearRequest();
if (tree.HasChildren())
{
foreach (var child in tree.Children)
{
var newPath = path.ToList();
newPath.Add(tree.Name);
WriteTree(client, request, child, newPath.ToArray());
}
}
}
}
public static void WriteResourceTree(IClient client, Request request, ObjectTreeBuilder tree, params string[] path)
{
if (tree != null)
{
if (path != null && path.Length > 0)
request.SetPath(path);
var res = tree.Value.FirstOrDefault();
if (res != null)
{
CSEBase.CRUD(client, request, oneM2M.Operation.CREATE, GetResourceType(res), res);
request.ClearRequest();
}
if (res != null && tree.HasChildren())
{
foreach (var child in tree.Children)
{
var newPath = path.ToList();
newPath.Add(((resource)res).Rn);
WriteResourceTree(client, request, child, newPath.ToArray());
}
}
}
}
#endregion
#region Resource Handling (Converter, Instantiator)
public static object ConvertToBaseResource(object convenientResource)
{
if (convenientResource == null)
return null;
string conResTypeName = convenientResource.GetType().Name.LowercaseFirst();
string shortname = oneM2M.Name.LongToShort(conResTypeName).ToLower();
Type baseResourceType = Type.GetType(oneM2M.PRIMITIVE_PACKAGE + shortname, true, true);
object baseResource = Activator.CreateInstance(baseResourceType);
PropertyInfo[] convResField = convenientResource.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
for (int i = 0; i < convResField.Length; i++)
{
string longName = LowercaseFirst(convResField[i].Name);
string shortName = UppercaseFirst(oneM2M.Name.LongToShort(longName));
object value = convResField[i].GetValue(convenientResource);
if (value != null && !string.IsNullOrEmpty(shortName) && shortName != "Ty")
{
PropertyInfo propInfo = baseResource.GetType().GetProperty(shortName, BindingFlags.Instance | BindingFlags.Public);
if (propInfo != null)
{
if(value.GetType() != propInfo.PropertyType)
{
Type t = Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType;
try { value = Convert.ChangeType(value, t); } catch { value = null; }
}
var setMethod = propInfo.GetSetMethod();
if(setMethod != null)
propInfo.SetValue(baseResource, value, null);
}
}
}
return baseResource;
}
public static void ConvertToConvenientResource(this IResource convenientResource, object resource)
{
PropertyInfo[] resField = resource.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
for (int i = 0; i < resField.Length; i++)
{
string shortName = resField[i].Name.ToLower();
string longName = oneM2M.Name.ShortTolong(shortName).UppercaseFirst();
object value = resField[i].GetValue(resource);
if (value != null && !string.IsNullOrEmpty(longName))
{
PropertyInfo propInfo = convenientResource.GetType().GetProperty(longName, BindingFlags.Instance | BindingFlags.Public);
if (propInfo != null)
{
if (value is ICollection && (value as ICollection).Count == 0)
continue;
if (value.GetType() != propInfo.PropertyType)
{
Type t = Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType;
if (t.IsEnum)
value = Enum.Parse(t, value.ToString());
else
value = Convert.ChangeType(value, t);
}
var setMethod = propInfo.GetSetMethod();
if (setMethod != null)
propInfo.SetValue(convenientResource, value, null);
}
}
}
}
public static string UppercaseFirst(this string s)
{
if (string.IsNullOrEmpty(s))
{
return string.Empty;
}
return char.ToUpper(s[0]) + s.Substring(1);
}
public static string LowercaseFirst(this string s)
{
if (string.IsNullOrEmpty(s))
{
return string.Empty;
}
return char.ToLower(s[0]) + s.Substring(1);
}
public static object InstantiateResource(MethodBase m, int parameterIndex, Type t, params object[] args)
{
if (IsNullObjects(m, t))
return null;
ParameterInfo[] parameters = m.GetParameters();
object resource = Activator.CreateInstance(t);
for (int i = 0; i < parameters.Length - parameterIndex; i++)
{
string longName = parameters[i + parameterIndex].Name;
string shortName = UppercaseFirst(oneM2M.Name.LongToShort(longName));
object value = args[i];
if (!string.IsNullOrEmpty(shortName))
{
PropertyInfo propInfo = resource.GetType().GetProperty(shortName, BindingFlags.Instance | BindingFlags.Public);
if (propInfo != null)
{
propInfo.SetValue(resource, value, null);
}
}
else
throw new KeyNotFoundException(shortName + " is missing in the longToShort-Dictionary - longName: " + longName);
}
return resource;
}
public static cin GetLatestResource(ObjectTreeBuilder objectTreeBuilder)
{
if(objectTreeBuilder != null && objectTreeBuilder.HasChildren())
{
List<cin> cins = objectTreeBuilder.Children.Select(c => c.GetValue<cin>()).ToList();
var latestCreationTime = cins.Where(a => a != null)?.Max(c => oneM2M.Time.Parse(c.Ct));
var latestResource = cins.Where(a => a != null)?.FirstOrDefault(c => oneM2M.Time.Parse(c.Ct) == latestCreationTime);
return latestResource;
}
return null;
}
public static oneM2M.ResourceType GetResourceType(object resource)
{
switch (resource.GetType().Name.ToString())
{
case "cnt": return oneM2M.ResourceType.Container;
case "cin": return oneM2M.ResourceType.ContentInstance;
case "ae": return oneM2M.ResourceType.ApplicationEntity;
case "sub": return oneM2M.ResourceType.Subscription;
default:
return default(oneM2M.ResourceType);
}
}
#endregion
#region Miscellaneous
[Obsolete("Works only in openDaylight IoTDM")]
public static string GetResourceIdFromParentId(string resourceId)
{
if (!string.IsNullOrEmpty(resourceId))
{
string[] split = resourceId.Split('/');
if (split != null && split.Length >= 3)
return (split[2]);
else
return null;
}
else
return null;
}
public static bool IsPath(string path)
{
if (!string.IsNullOrEmpty(path))
if (path.Contains("/"))
return true;
return false;
}
public static bool IsId(string id)
{
if (!string.IsNullOrEmpty(id))
if (!id.Contains("/")) //ToDo: check regex
return true;
return false;
}
public static Result<T> HandleResultWithException<T>(Result<T> result, Exception e)
{
if (result == null)
return new Result<T>(false, default(T), new Message(MessageType.Error, e.Message));
else
{
var newResult = new Result<T>(e);
newResult.Messages.AddRange(result.Messages);
return result;
}
}
public static bool IsNullObjects(params object[] args)
{
for (int i = 0; i < args.Length; i++)
{
if (args[i] == null)
return true;
}
return false;
}
#endregion
}
}