/// <summary>
        /// Writes an exception into one or more log entries, storing them in entries with a given prefix
        /// </summary>
        /// <param name="source">Log source</param>
        /// <param name="ex">Exception</param>
        /// <param name="entries">Log entry list</param>
        /// <param name="prefix">Entry string prefix</param>
        public static void ToLogEntries( Source source, Exception ex, IList< Entry > entries, string prefix )
        {
            if ( ex == null )
            {
                return;
            }
            StackTraceInfo info = new StackTraceInfo( ex.StackTrace );

            Entry baseEntry = new Entry( source, prefix + ex.Message );
            if ( info.HasMatch )
            {
                baseEntry.Locate( info.File, int.Parse( info.Line ), 1, info.Method );
                info.NextMatch( );
            }

            entries.Add( baseEntry );

            while ( info.HasMatch )
            {
                entries.Add( new Entry( source, prefix + "(call stack)" ).Locate( info.File, int.Parse( info.Line ), 1, info.Method ) );
                info.NextMatch( );
            }

            ToLogEntries( source, ex.InnerException, entries, prefix + "\t" );
        }
        /// <summary>
        /// Loads component XML from a stream
        /// </summary>
        public object Load( Stream stream, ISource source, LoadParameters parameters )
        {
            if ( !( parameters is ComponentLoadParameters ) )
            {
                ComponentLoadParameters newParameters = new ComponentLoadParameters( parameters.Target );

                foreach ( IDynamicProperty property in parameters.Properties )
                {
                    newParameters.Properties.Add( property );
                }

                parameters = newParameters;
            }

            parameters.CanCache = false;

            ErrorCollection errors = new ErrorCollection( string.Copy( source.Path ) );

            XmlTextReader reader = new XmlTextReader( stream );
            reader.WhitespaceHandling = WhitespaceHandling.Significant;
            try
            {
                if ( reader.MoveToContent( ) == XmlNodeType.None )
                {
                    AssetsLog.Warning( "XML component asset \"{0}\" was empty - returning null", source.Name );
                    return null;
                }
            }
            catch ( XmlException ex )
            {
                AssetsLog.Error( "Moving to XML component asset \"{0}\" content threw an exception", source.Name );

                Entry entry = new Entry( AssetsLog.GetSource( Severity.Error ), ex.Message );
                Source.HandleEntry( entry.Locate( source.Path, ex.LineNumber, ex.LinePosition, "" ) );

                throw new ApplicationException( string.Format( "Failed to load component XML asset \"{0}\" (see log for details)", source.Name ) );
            }

            string cacheable = reader.GetAttribute( "cacheable" );
            parameters.CanCache = ( cacheable != null ) && ( ( cacheable == "yes" ) || ( cacheable == "true" ) );

            RootBuilder builder = ( RootBuilder )BaseBuilder.CreateBuilderFromReader( null, ( ComponentLoadParameters )parameters, errors, reader );

            if ( errors.Count == 0 )
            {
                BaseBuilder.SafePostCreate( builder );
                if ( errors.Count == 0 )
                {
                    BaseBuilder.SafeResolve( builder, true );
                }
            }

            if ( ( builder.BuildObject == null ) && ( errors.Count == 0 ) )
            {
                errors.Add( builder, "Empty components file" );
            }

            if ( errors.Count > 0 )
            {
                foreach ( Entry error in errors )
                {
                    Source.HandleEntry( error );
                }
                throw new ApplicationException( string.Format( "Failed to load component XML asset \"{0}\" (see log for details)", source.Name ) );
            }

            //	TODO: AP: bit dubious... if there's more than one object, return a list
            if ( builder.Children.Count == 0 )
            {
                throw new ApplicationException( string.Format( "Failed to load component XML asset \"{0}\" - did not contain any components", source.Name ) );
            }
            if ( builder.Children.Count == 1 )
            {
                return builder.Children[ 0 ];
            }
            return builder.Children;
        }
        /// <summary>
        /// Loads code from a source
        /// </summary>
        public override object Load( ISource source, LoadParameters parameters )
        {
            string typeToInstance = DynamicProperties.GetProperty< string >( parameters.Properties, InstanceTypeName, null );
            IEnumerable< string > assemblies = DynamicProperties.GetProperty< IEnumerable< string > >( parameters.Properties, ReferencesName, new string[] { } );

            using ( Stream stream = OpenStream( source ) )
            {
                StreamReader reader = new StreamReader( stream );
                string allLines = reader.ReadToEnd( );

                CodeDomProvider provider = new CSharpCodeProvider( );
                CompilerParameters compilerParams = new CompilerParameters( );
                compilerParams.GenerateInMemory = true;

            #if DEBUG
                compilerParams.IncludeDebugInformation = true;
            #endif
                //	Add referenced assemblies to compiler parameters
                foreach ( string assembly in assemblies )
                {
                    compilerParams.ReferencedAssemblies.Add( assembly );
                }
                CompilerResults results = provider.CompileAssemblyFromSource( compilerParams, allLines );
                if ( results.Errors.Count > 0 )
                {
                    //	Failed to compile assembly - dump
                    foreach ( CompilerError error in results.Errors )
                    {
                        Entry entry = new Entry( AssetsLog.GetSource( error.IsWarning ? Severity.Warning : Severity.Error ), error.ErrorText );
                        entry.Locate( error.FileName, error.Line, error.Column, "" );
                        Source.HandleEntry( entry );
                    }
                    return null;
                }

                //	If the caller requested the instance of a specific type, then find that type in the
                //	compiled assembly, and create an instance of it
                if ( typeToInstance != null )
                {
                    Type instanceType = results.CompiledAssembly.GetType( typeToInstance );
                    return Activator.CreateInstance( instanceType );
                }

                //	Find a type that implements IBuilder, instance it, then use the IBuilder.CreateInstance()
                //	to create our required type
                foreach ( Type type in results.CompiledAssembly.GetTypes( ) )
                {
                    if ( typeof( IBuilder ).IsAssignableFrom( type ) )
                    {
                        IBuilder builder = ( IBuilder )Activator.CreateInstance( type );
                        return builder.CreateInstance( type );
                    }
                }
            }

            return null;
        }
        /// <summary>
        /// Generates a log entry, with a specified stack frame offset as location
        /// </summary>
        /// <param name="skip">Number of stack frames to skip to get the location</param>
        /// <param name="msg">Message string</param>
        /// <param name="args">Format arguments</param>
        public void Write( int skip, string msg, params object[ ] args )
        {
            if ( !m_Suppress )
            {
                //  Create a new log entry
                //	(skip gets 2 extra to cope with the Locate() call, and this call)
                Entry newEntry = new Entry( this, string.Format( msg, args ) ).Locate( skip + 2 );

                HandleEntry( newEntry );
            }
        }
        /// <summary>
        /// Handles a new log entry
        /// </summary>
        /// <param name="entry">New log entry</param>
        public static void HandleEntry( Entry entry )
        {
            System.Diagnostics.Trace.WriteLine( entry.ToString( ) );

            if ( OnNewLogEntry != null )
            {
                OnNewLogEntry( entry );
            }
        }
        /// <summary>
        /// Creates an array of Entry objects by parsing the contents of a log stream
        /// </summary>
        /// <param name="reader">Log text reader</param>
        /// <returns>Returns entry array</returns>
        public static Entry[] CreateEntriesFromLogText( TextReader reader )
        {
            List< Entry > entries = new List< Entry >( );

            string text = reader.ReadToEnd( );
            MatchCollection matches = LogEntryRegex.Matches( text );
            //int numMatches = matches.Count;

            foreach ( Match curMatch in matches )
            {
                string file     = curMatch.Groups[ "File" ].Value;
                string line     = curMatch.Groups[ "Line" ].Value;
                string column   = curMatch.Groups[ "Column" ].Value;
                string source   = curMatch.Groups[ "Source" ].Value;
                string message  = curMatch.Groups[ "Message" ].Value;
                string thread   = curMatch.Groups[ "Thread" ].Value;
                string method   = curMatch.Groups[ "Method" ].Value;

                //  TODO: AP: Time not written/read
                Entry newEntry = new Entry( Log.Source.BuildFromString( source ), message, thread, DateTime.Now.TimeOfDay );
                newEntry.Locate( file, int.Parse( line ), int.Parse( column ), method );
                entries.Add( newEntry );
            }

            return entries.ToArray( );
        }