/* * 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; } } } }