#region License

/*
 * Copyright 2004 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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.
 */

#endregion

#region Imports

using System;
using System.Xml;
using NUnit.Framework;
using Spring.Core.IO;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
using Spring.Validation;

#endregion

namespace Spring.Objects.Factory.Xml
{
    /// <summary>
    /// Unit tests for the XmlObjectDefinitionReader class.
    /// </summary>
    /// <author>Rick Evans (.NET)</author>
    [TestFixture]
    public class XmlObjectDefinitionReaderTests
    {
        [Test]
        public void Instantiation()
        {
            XmlObjectDefinitionReader reader
                = new XmlObjectDefinitionReader(
                    new DefaultListableObjectFactory());
        }

        [Test]
        [ExpectedException(typeof(ObjectDefinitionStoreException))]
        public void LoadObjectDefinitionsWithNullResource()
        {
            XmlObjectDefinitionReader reader
                = new XmlObjectDefinitionReader(
                    new DefaultListableObjectFactory());
            reader.LoadObjectDefinitions((string)null);
        }

        [Test]
        [ExpectedException(typeof(ObjectDefinitionStoreException))]
        public void LoadObjectDefinitionsWithNonExistentResource()
        {
            XmlObjectDefinitionReader reader
                = new XmlObjectDefinitionReader(
                    new DefaultListableObjectFactory());
            reader.LoadObjectDefinitions(new ReadOnlyXmlTestResource("/dev/null"));
        }

        [Test]
        public void AutoRegistersWellknownNamespaceParser()
        {
            try
            {
                NamespaceParserRegistry.Reset();

                DefaultListableObjectFactory of = new DefaultListableObjectFactory();
                XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
                reader.LoadObjectDefinitions(new StringResource(
                                                 @"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net' 
         xmlns:v='http://www.springframework.net/validation'>  
      <v:required id='tripValidator' test='true' />
</objects>
"));
                Assert.AreEqual(typeof(RequiredValidator), of.GetObject("tripValidator").GetType());
            }
            finally
            {
                NamespaceParserRegistry.Reset();                
            }
        }

        [Test]
        [ExpectedException(typeof(ObjectDefinitionStoreException))]
        public void ThrowsOnUnknownNamespaceUri()
        {
            NamespaceParserRegistry.Reset();

            DefaultListableObjectFactory of = new DefaultListableObjectFactory();
            XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
            reader.LoadObjectDefinitions(new StringResource(
                                             @"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net' 
     xmlns:x='http://www.springframework.net/XXXX'>  
  <x:group id='tripValidator' />
</objects>
"));
            Assert.Fail();
        }


        [Test]
        public void WhitespaceValuesArePreservedForValueAttribute()
        {
            DefaultListableObjectFactory of = new DefaultListableObjectFactory();
            XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
            reader.LoadObjectDefinitions(new StringResource(
                @"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>  
	<object id='test' type='Spring.Objects.TestObject, Spring.Core.Tests'>
		<property name='name' value=' &#x000a;&#x000d;&#x0009;' />
	</object>
</objects>
"));
            Assert.AreEqual(" \n\r\t", ((TestObject) of.GetObject("test")).Name);
        }

        [Test]
        public void WhitespaceValuesResultInEmptyStringForValueElement()
        {
            DefaultListableObjectFactory of = new DefaultListableObjectFactory();
            XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
            reader.LoadObjectDefinitions(new StringResource(
                @"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>  
	<object id='test2' type='Spring.Objects.TestObject, Spring.Core.Tests'>
		<property name='name'><value /></property>
	</object>
	<object id='test3' type='Spring.Objects.TestObject, Spring.Core.Tests'>
		<property name='name'><value></value></property>
	</object>
	<object id='test4' type='Spring.Objects.TestObject, Spring.Core.Tests'>
		<property name='name'><value xml:space='default'> &#x000a;&#x000d;&#x0009;</value></property>
	</object>
</objects>
"));
            Assert.AreEqual(string.Empty, ((TestObject) of.GetObject("test2")).Name);
            Assert.AreEqual(string.Empty, ((TestObject) of.GetObject("test3")).Name);
            Assert.AreEqual(string.Empty, ((TestObject) of.GetObject("test4")).Name);
        }


        [Test]
        public void WhitespaceValuesArePreservedForValueElementWhenSpaceIsSetToPreserve()
        {
            DefaultListableObjectFactory of = new DefaultListableObjectFactory();
            XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
            reader.LoadObjectDefinitions(new StringResource(
                @"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>  
	<object id='test4' type='Spring.Objects.TestObject, Spring.Core.Tests'>
		<property name='name'><value xml:space='preserve'> &#x000a;&#x000d;&#x0009;</value></property>
	</object>
</objects>
"));
            Assert.AreEqual(" \n\r\t", ((TestObject) of.GetObject("test4")).Name);
        }

        [Test]
        public void ThrowsObjectDefinitionStoreExceptionOnValidationError()
        {
            try 
            {
                DefaultListableObjectFactory of = new DefaultListableObjectFactory();
                XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
                reader.LoadObjectDefinitions(new StringResource(
                                                @"<?xml version='1.0' encoding='UTF-8' ?>
                                                <objects xmlns='http://www.springframework.net'>  
	                                                <INVALIDELEMENT id='test2' type='Spring.Objects.TestObject, Spring.Core.Tests' />
                                                </objects>
                                                "));                       
                Assert.Fail();
            }
            catch(ObjectDefinitionStoreException ex)
            {
                Assert.IsTrue( ex.Message.IndexOf("Line 3 in XML document from  violates the schema.") > -1);
            }
        }

        [Test]
        public void ThrowsObjectDefinitionStoreExceptionOnInvalidXml()
        {
            try
            {
                DefaultListableObjectFactory of = new DefaultListableObjectFactory();
                XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
                reader.LoadObjectDefinitions(new StringResource(
                                                 @"<?xml version='1.0' encoding='UTF-8' ?>
                                                <objects xmlns='http://www.springframework.net'>  
	                                                <object id='test2' type='Spring.Objects.TestObject, Spring.Core.Tests'>
                                                </objects>
                                                "));
                Assert.Fail();
            }
            catch(ObjectDefinitionStoreException ex)
            {
                Assert.IsTrue( ex.Message.IndexOf("Line 4 in XML document from  is not well formed.") > -1);
            }
        }

        #region ThrowsObjectDefinitionStoreExceptionOnErrorDuringObjectDefinitionRegistration Helper

        private class TestXmlObjectDefinitionReader : XmlObjectDefinitionReader
        {
            public TestXmlObjectDefinitionReader(IObjectDefinitionRegistry registry) : base(registry)
            {}

            private class ThrowingObjectDefinitionDocumentReader : IObjectDefinitionDocumentReader
            {
                public void RegisterObjectDefinitions(XmlDocument doc, XmlReaderContext readerContext)
                {
                    throw new TestException("RegisterObjectDefinitions");
                }
            }

            protected override IObjectDefinitionDocumentReader CreateObjectDefinitionDocumentReader()
            {
                return new ThrowingObjectDefinitionDocumentReader();
            }

        }

        #endregion

        [Test]
        [ExpectedException(typeof(ObjectDefinitionStoreException))]
        public void ThrowsObjectDefinitionStoreExceptionOnErrorDuringObjectDefinitionRegistration()
        {
            DefaultListableObjectFactory of = new DefaultListableObjectFactory();
            XmlObjectDefinitionReader reader = new TestXmlObjectDefinitionReader(of);
            reader.LoadObjectDefinitions(new StringResource(
                @"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>  
	<object id='test2' type='Spring.Objects.TestObject, Spring.Core.Tests' />
</objects>
"));                       
        }

        [Test]
        public void ParsesNonDefaultNamespace()
        {
            try
            {
                NamespaceParserRegistry.Reset();

                DefaultListableObjectFactory of = new DefaultListableObjectFactory();
                XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
                reader.LoadObjectDefinitions(new StringResource(
@"<?xml version='1.0' encoding='UTF-8' ?>
<core:objects xmlns:core='http://www.springframework.net'>  
	<core:object id='test2' type='Spring.Objects.TestObject, Spring.Core.Tests'>
        <core:property name='Sibling'>
            <core:object type='Spring.Objects.TestObject, Spring.Core.Tests' />
        </core:property>
    </core:object>
</core:objects>
"));
                TestObject test2 = (TestObject) of.GetObject("test2");
                Assert.AreEqual(typeof(TestObject), test2.GetType());
                Assert.IsNotNull(test2.Sibling);
            }
            finally
            {
                NamespaceParserRegistry.Reset();
            }
        }

        [Test]
        public void ParsesObjectAttributes()
        {
            DefaultListableObjectFactory of = new DefaultListableObjectFactory();
            XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(of);
            reader.LoadObjectDefinitions(new StringResource(
@"<?xml version='1.0' encoding='UTF-8' ?>
<objects xmlns='http://www.springframework.net'>  
	<object id='test1' type='Spring.Objects.TestObject, Spring.Core.Tests' singleton='false' abstract='true' />
	<object id='test2' type='Spring.Objects.TestObject, Spring.Core.Tests' singleton='true' abstract='false' lazy-init='true' 
        autowire='no' dependency-check='simple'
        depends-on='test1' 
        init-method='init'
        destroy-method='destroy'
    />
</objects>
"));
            AbstractObjectDefinition od1 = (AbstractObjectDefinition) of.GetObjectDefinition("test1");
            Assert.IsFalse(od1.IsSingleton);
            Assert.IsTrue(od1.IsAbstract);
            Assert.IsFalse(od1.IsLazyInit);

            AbstractObjectDefinition od2 = (AbstractObjectDefinition) of.GetObjectDefinition("test2");
            Assert.IsTrue(od2.IsSingleton);
            Assert.IsFalse(od2.IsAbstract);
            Assert.IsTrue(od2.IsLazyInit);
            Assert.AreEqual(AutoWiringMode.No, od2.AutowireMode);
            Assert.AreEqual("init", od2.InitMethodName);
            Assert.AreEqual("destroy", od2.DestroyMethodName);
            Assert.AreEqual(1, od2.DependsOn.Length);
            Assert.AreEqual("test1", od2.DependsOn[0]);
            Assert.AreEqual(DependencyCheckingMode.Simple, od2.DependencyCheck);
        }
    }
}