/*
* Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using Amazon.Runtime.Internal;
using Amazon.Runtime.Internal.Util;
using Amazon.Util.Internal;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Xml.Linq;
using UnityEngine;
using Logger = Amazon.Runtime.Internal.Util.Logger;
namespace Amazon
{
public static partial class AWSConfigs
{
#region ApplicationName
///
/// The unique application name for the current application. This values is currently used
/// by high level APIs (Mobile Analytics Manager and Cognito Sync Manager) to create a unique file
/// path to store local database files.
/// Changes to this setting will only take effect in newly-constructed objects using this property.
///
/// <configSections>
/// <section name="aws" type="Amazon.AWSSection, AWSSDK"/>
/// </configSections>
/// <aws applicationName="" />
///
///
public static string ApplicationName
{
get { return _rootConfig.ApplicationName; }
set { _rootConfig.ApplicationName = value; }
}
#endregion
#region public api's
///
/// For backward compatibility for App.Config on .Net35 and .Net45 platforms only. This functions returns Null in Unity
///
///
///
public static string GetConfig(string name)
{
return null;
}
private static HttpClientOption _httpClient;
///
/// The Unity Api used for making HTTP calls. Defaults to WWW.
/// UnityWebRequest API is allows you access to more AWS Services
/// but is restricted to Versions of Unity > 5.3
///
public static HttpClientOption HttpClient
{
get
{
return _httpClient;
}
set
{
if (value == HttpClientOption.UnityWebRequest)
{
if (!UnityWebRequestWrapper.IsUnityWebRequestSupported)
{
UnityWebRequestInitialized = false;
throw new InvalidOperationException("UnityWebRequest is not supported in the current version of unity");
}
else
{
UnityWebRequestInitialized = true;
}
}
_httpClient = value;
}
}
internal static bool UnityWebRequestInitialized;
///
/// The Unity Api used for making HTTP calls
///
public enum HttpClientOption
{
///
/// Uses UnityEngine.WWW
///
UnityWWW = 0,
///
/// Uses UnityEngine.Experimental.Networking.UnityWebRequest
///
UnityWebRequest = 1
}
#endregion
#region internal methods
internal static T GetSection(string sectionName)
where T : AWSSection, new()
{
if (!configPresent)
return new T();
if (xmlDoc == null)
{
lock (_lock)
{
if (xmlDoc == null)
{
xmlDoc = LoadConfigFromResource();
configPresent = (xmlDoc != null);
}
}
}
if (configPresent)
{
XElement sectionElement = xmlDoc.Element(sectionName);
T t = new T
{
Logging = GetObject(sectionElement, "logging"),
Region = sectionElement.Attribute("region") == null ? string.Empty : sectionElement.Attribute("region").Value,
CorrectForClockSkew = bool.Parse(sectionElement.Attribute("correctForClockSkew").Value),
ServiceSections = GetUnresolvedElements(sectionElement)
};
return t;
}
return new T();
}
internal static bool XmlSectionExists(string sectionName)
{
return configPresent && xmlDoc.Element(sectionName) != null;
}
#endregion
#region tracelistener
internal static Dictionary> _traceListeners
= new Dictionary>(StringComparer.OrdinalIgnoreCase);
///
/// Add a listener for SDK logging.
///
/// If the listener does not have a name, you will not be able to remove it later.
/// The source to log for, e.g. "Amazon", or "Amazon.DynamoDB".
/// The listener to add.
public static void AddTraceListener(string source, TraceListener listener)
{
if (string.IsNullOrEmpty(source))
throw new ArgumentException("Source cannot be null or empty", "source");
if (null == listener)
throw new ArgumentException("Listener cannot be null", "listener");
lock (_traceListeners)
{
if (!_traceListeners.ContainsKey(source))
_traceListeners.Add(source, new List());
_traceListeners[source].Add(listener);
}
}
///
/// Remove a trace listener from SDK logging.
///
/// The source the listener was added to.
/// The name of the listener.
public static void RemoveTraceListener(string source, string name)
{
if (string.IsNullOrEmpty(source))
throw new ArgumentException("Source cannot be null or empty", "source");
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Name cannot be null or empty", "name");
lock (_traceListeners)
{
if (_traceListeners.ContainsKey(source))
{
foreach (var l in _traceListeners[source])
{
if (l.Name.Equals(name, StringComparison.Ordinal))
{
_traceListeners[source].Remove(l);
break;
}
}
}
}
Logger.ClearLoggerCache();
}
// Used by Logger.Diagnostic to add listeners to TraceSources when loggers
// are created.
internal static TraceListener[] TraceListeners(string source)
{
lock (_traceListeners)
{
List temp;
if (_traceListeners.TryGetValue(source, out temp))
{
return temp.ToArray();
}
return new TraceListener[0];
}
}
#endregion
#region private methods
const string CONFIG_FILE = "awsconfig";
internal static XDocument xmlDoc;
private static XDocument LoadConfigFromResource()
{
XDocument xDoc = null;
TextAsset awsConfig = null;
Action action = () =>
{
awsConfig = Resources.Load(CONFIG_FILE) as TextAsset;
if (awsConfig != null && awsConfig.bytes.Count() > 0)
{
using (Stream stream = new MemoryStream(awsConfig.bytes))
{
using (StreamReader reader = new StreamReader(stream))
{
xDoc = XDocument.Load(reader);
}
}
}
};
if (UnityInitializer.IsMainThread())
{
action();
}
else
{
ManualResetEvent e = new ManualResetEvent(false);
UnityRequestQueue.Instance.ExecuteOnMainThread(() =>
{
action();
e.Set();
});
e.WaitOne();
}
return xDoc;
}
public static T GetObject(XElement rootElement, string propertyName) where T : class, new()
{
return (T)GetObject(rootElement, propertyName, typeof(T));
}
private static object GetObject(XElement rootElement, string propertyName, Type type)
{
object t = Activator.CreateInstance(type);
var propertyInfos = t.GetType().GetProperties();
rootElement = rootElement.Elements(propertyName).Count() == 0 ?
rootElement : rootElement.Elements(propertyName).First();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (propertyInfo.CanWrite)
{
var propertyType = propertyInfo.PropertyType;
// Try to find a matching attribute
var attrib = rootElement.Attributes().SingleOrDefault(
a => a.Name.ToString().Equals(propertyInfo.Name, StringComparison.OrdinalIgnoreCase));
if (attrib != null)
{
if (propertyType.BaseType.Equals(typeof(Enum)))
{
propertyInfo.SetValue(t, Enum.Parse(propertyType, attrib.Value, true), null);
}
else
{
// Check if the type is nullable
var underlyingType = Nullable.GetUnderlyingType(propertyType);
object result;
if (underlyingType != null)
{
result = Convert.ChangeType(attrib.Value, underlyingType);
}
else
{
// Converters for types which Convert.ChangeType
// can't handle.
if (propertyType == typeof(Type))
{
result = Type.GetType(attrib.Value, true);
}
else
{
result = Convert.ChangeType(attrib.Value, propertyType);
}
}
propertyInfo.SetValue(t, result, null);
}
continue;
}
// Try to find a matching child element if a matching attribute was not found
var element = rootElement.Elements().SingleOrDefault(
e => e.Name.ToString().Equals(propertyInfo.Name, StringComparison.OrdinalIgnoreCase));
// Process lists
if (typeof(IList).IsAssignableFrom(propertyType))
{
// Pass rootElement instead of element so that
// we can support XML lists items where the enclosing tag
// is not present
var listPropertyName = element == null ? null : element.Name.ToString();
var result = GetList(rootElement, propertyType, listPropertyName);
propertyInfo.SetValue(t, result, null);
continue;
}
// Process complex child elements
if (element != null)
{
var result = GetObject(element, element.Name.ToString(), propertyType);
propertyInfo.SetValue(t, result, null);
}
}
}
return t;
}
private static IEnumerable GetList(XElement rootElement, Type listType, string propertyName)
{
var list = (IList)Activator.CreateInstance(listType);
var itemNamePropInfo = listType.GetProperty("ItemPropertyName");
var itemName = (string)itemNamePropInfo.GetValue(list, null);
var itemType = listType.GetProperty("Item").PropertyType;
if (!string.IsNullOrEmpty(propertyName))
{
rootElement = rootElement.Elements(propertyName).Count() == 0 ?
rootElement : rootElement.Elements(propertyName).First();
}
// Process list items
foreach (var childElement in rootElement.Elements())
{
var item = GetObject(childElement, itemName, itemType);
list.Add(item);
}
return list;
}
private static IDictionary GetUnresolvedElements(XElement parent)
{
IDictionary unresolvedElemets = new Dictionary();
foreach (XElement element in parent.Elements())
{
if (!standardConfigs.Contains(element.Name.ToString()))
unresolvedElemets.Add(element.Name.ToString(), element);
}
return unresolvedElemets;
}
#endregion
}
}
namespace Amazon.Util.Internal
{
public abstract class ConfigurationElement
{
public ConfigurationElement()
{
this.ElementInformation = new ElementInformation(true);
}
public ElementInformation ElementInformation { get; set; }
}
public class ElementInformation
{
public ElementInformation(bool isPresent)
{
this.IsPresent = isPresent;
}
public bool IsPresent { get; private set; }
}
public abstract class ConfigurationList : List
{
public IEnumerable Items { get { return this; } }
}
}