context: #region License
  
  // Copyright (c) 2013, ClearCanvas Inc.
  // All rights reserved.
  // http://www.clearcanvas.ca
  //
  // This file is part of the ClearCanvas RIS/PACS open source project.
  //
  // The ClearCanvas RIS/PACS open source project is free software: you can
  // redistribute it and/or modify it under the terms of the GNU General Public
  // License as published by the Free Software Foundation, either version 3 of the
  // License, or (at your option) any later version.
  //
  // The ClearCanvas RIS/PACS open source project is distributed in the hope that it
  // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
  // Public License for more details.
  //
  // You should have received a copy of the GNU General Public License along with
  // the ClearCanvas RIS/PACS open source project.  If not, see
  // <http://www.gnu.org/licenses/>.
  
  #endregion
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Globalization;
  using System.IO;
  using System.Security;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Xml;
  using ClearCanvas.Common;
  
  namespace ClearCanvas.Dicom.Utilities.Xml
  {
      public class SourceImageInfo
      {
          public string SopInstanceUid { get; set; }
      }
  
  	/// <summary>
  	/// Class for representing a SOP Instance as XML.
  	/// </summary>
  	/// <remarks>
  	/// This class may change in a future release.
  	/// </remarks>
  	public class InstanceXml
  	{
  		#region Private members
  
  		private readonly String _sopInstanceUid;
  		private readonly SopClass _sopClass;
  		private readonly TransferSyntax _transferSyntax;
          private string _sourceAETitle;
          private string _sourceFileName;
  		private long _fileSize = 0;
  
  		private BaseInstanceXml _baseInstance;
  		private XmlElement _cachedElement;
  
  		private DicomAttributeCollection _collection;
  
  		private IEnumerator<DicomAttribute> _baseCollectionEnumerator;
  		private IEnumerator _instanceXmlEnumerator;
  
  	    private IList<SourceImageInfo> _sourceImageInfoList;
  	    private bool _sourceImageInfoListLoaded;
  
  	    public IList<SourceImageInfo> SourceImageInfoList
  	    {
  	        get
  	        {
                  if (!_sourceImageInfoListLoaded)
                  {
                      _sourceImageInfoList = LoadSourceImageInfo(Collection);
                      _sourceImageInfoListLoaded = true;
                  }
  	            return _sourceImageInfoList;
  	        } 
              private set
              {
                  _sourceImageInfoList = value;
              }
  	    }
  
  		#endregion
  
  		#region Public Properties
  
  		public String SopInstanceUid
  		{
  			get
  			{
  				return _sopInstanceUid ?? "";
  			}
  		}
  
  		public SopClass SopClass
  		{
  			get { return _sopClass; }
  		}
  
  		public string SourceFileName
  		{
  			get { return _sourceFileName; }
  			internal set
  			{
  				if (value != null && Path.IsPathRooted(value))
  					value = Path.GetFullPath(value);
  
  				_sourceFileName = value;
  			}
  		}
  
  	    public string SourceAETitle
  	    {
              get { return _sourceAETitle; }
              set { _sourceAETitle = value; }
  	    }
  
  	    public long FileSize
  		{
  			get { return _fileSize; }
  			set { _fileSize = value; }
  		}
  
  		public TransferSyntax TransferSyntax
  		{
  			get { return _transferSyntax; }
  		}
  
  		/// <summary>
  		/// Gets the underlying data as a <see cref="DicomAttributeCollection"/>.
  		/// </summary>
  		/// <remarks>
  		/// When parsed from xml, the return type is <see cref="InstanceXmlDicomAttributeCollection"/>, otherwise
  		/// it is the source <see cref="DicomAttributeCollection"/>.
  		/// </remarks>
  		public DicomAttributeCollection Collection
  		{
  			get
  			{
  				ParseTo(0xffffffff);
  				return _collection;
  			}
  		}
  
  		public DicomAttribute this[DicomTag tag]
  		{
  			get
  			{
  				ParseTo(tag.TagValue);
  				return _collection[tag.TagValue];
  			}
  		}
  
  		public DicomAttribute this[uint tag]
  		{
  			get
  			{
  				ParseTo(tag);
  				return _collection[tag];
  			}
  		}
  
  		public bool IsTagExcluded(uint tag)
  		{
  			if (_collection is IInstanceXmlDicomAttributeCollection)
  			{
  				ParseTo(tag);
  				return ((IInstanceXmlDicomAttributeCollection) _collection).IsTagExcluded(tag);
  			}
  			return false;
  		}
  
  		#endregion
  
  		#region Constructors
  
  	    public InstanceXml(DicomAttributeCollection collection, SopClass sopClass, TransferSyntax syntax)
  		{
  			_sopInstanceUid = collection[DicomTags.SopInstanceUid];
  
  			_collection = collection;
  			_sopClass = sopClass;
  			_transferSyntax = syntax;
  
  			_collection.ValidateVrValues = false;
  			_collection.ValidateVrLengths = false;
  		}
  
          private static IList<SourceImageInfo> LoadSourceImageInfo(DicomAttributeCollection collection)
  	    {
  	        if (collection.Contains(DicomTags.SourceImageSequence))
  	        {
  	            DicomAttributeSQ sq = collection[DicomTags.SourceImageSequence] as DicomAttributeSQ;
  	            IList<SourceImageInfo> list = new List<SourceImageInfo>();
  	            foreach(DicomSequenceItem item in sq.Values as DicomSequenceItem[])
  	            {
                      list.Add(new SourceImageInfo()
  	                                            {SopInstanceUid = item[DicomTags.ReferencedSopInstanceUid].ToString()});
  
  	            }
                  return list;
  	        }
  
              return null;
  	    }
  
  	    public InstanceXml(XmlNode instanceNode, DicomAttributeCollection baseCollection)
  		{
  			InstanceXmlDicomAttributeCollection thisCollection = new InstanceXmlDicomAttributeCollection();
  			_collection = thisCollection;
  			_collection.ValidateVrValues = false;
  			_collection.ValidateVrLengths = false;
  
  			if (baseCollection != null)
  			{
  				AddExcludedTagsFromBase(baseCollection);
  
  				_baseCollectionEnumerator = baseCollection.GetEnumerator();
  				if (!_baseCollectionEnumerator.MoveNext())
  					_baseCollectionEnumerator = null;
  			}
  
  			if (!instanceNode.HasChildNodes)
  				return;
  
  			_instanceXmlEnumerator = instanceNode.ChildNodes.GetEnumerator();
  			if (!_instanceXmlEnumerator.MoveNext())
  				_instanceXmlEnumerator = null;
  
  			if (instanceNode.Attributes["UID"] != null)
  			{
  				_sopInstanceUid = instanceNode.Attributes["UID"].Value;
  			}
  
              if (instanceNode.Attributes["SourceAETitle"] != null)
              {
                  _sourceAETitle = XmlUnescapeString(instanceNode.Attributes["SourceAETitle"].Value);
              }
  
  			if (instanceNode.Attributes["SopClassUID"] != null)
  			{
  				_sopClass = SopClass.GetSopClass(instanceNode.Attributes["SopClassUID"].Value);
  			}
  
  			_transferSyntax = instanceNode.Attributes["TransferSyntaxUID"] != null 
  				? TransferSyntax.GetTransferSyntax(instanceNode.Attributes["TransferSyntaxUID"].Value) 
  				: TransferSyntax.ExplicitVrLittleEndian;
  
  			if (instanceNode.Attributes["SourceFileName"] != null)
  			{
  				_sourceFileName = instanceNode.Attributes["SourceFileName"].Value;
  			}
  
  			if (instanceNode.Attributes["FileSize"] != null)
  			{
  				long.TryParse(instanceNode.Attributes["FileSize"].Value, out _fileSize);
  			}
  
  			// This should never happen
  			if (_sopClass == null)
  			{
  				_sopClass = SopClass.GetSopClass(Collection[DicomTags.SopClassUid].GetString(0, String.Empty));
  			}
  
  		}
  
  		#endregion
  
  		#region Internal Methods
  
  		internal XmlElement GetMemento(XmlDocument theDocument, StudyXmlOutputSettings settings)
  		{
  			if (_cachedElement != null)
  			{
  				return _cachedElement;
  			}
  
  			if (_baseInstance != null)
  			{
  				_cachedElement = GetMementoForCollection(theDocument, _baseInstance.Collection, Collection, settings);
  
  				// Only keep around the cached xml data, free the collection to reduce memory usage
  				SwitchToCachedXml();
  
  				return _cachedElement;
  			}
  
  			_cachedElement = GetMementoForCollection(theDocument, null, Collection, settings);
  			return _cachedElement;
  		}
  
  		internal void SetBaseInstance(BaseInstanceXml baseInstance)
  		{
  			if (_baseInstance != baseInstance)
  			{
  				// force the element to be regenerated when GetMemento() is called
  				_cachedElement = null;
  			}
  
  			_baseInstance = baseInstance;
  		}
  
  		#endregion
  
  
  		#region Private Methods
  
  		private void AddExcludedTagsFromBase(DicomAttributeCollection baseCollection)
  		{
  			if (baseCollection is IPrivateInstanceXmlDicomAttributeCollection)
  			{
  				if (_collection is IPrivateInstanceXmlDicomAttributeCollection)
  				{
  					((IPrivateInstanceXmlDicomAttributeCollection)_collection).ExcludedTagsHelper.Add(
  						((IPrivateInstanceXmlDicomAttributeCollection)baseCollection).ExcludedTags);
  				}
  			}
  		}
  
  		private static StudyXmlTagInclusion AttributeShouldBeIncluded(DicomAttribute attribute, StudyXmlOutputSettings settings)
  		{
  			if (settings == null)
  				return StudyXmlTagInclusion.IncludeTagValue;
  
  			if (attribute is DicomAttributeSQ)
  			{
  				if (attribute.Tag.IsPrivate)
  					return settings.IncludePrivateValues;
  				return StudyXmlTagInclusion.IncludeTagValue;
  			}
  
  
  			// private tag
  			if (attribute.Tag.IsPrivate)
  			{
  				if (settings.IncludePrivateValues != StudyXmlTagInclusion.IncludeTagValue)
  					return settings.IncludePrivateValues;
  			}
  
  			// check type
  			if (attribute is DicomAttributeUN)
  			{
  				if (settings.IncludeUnknownTags != StudyXmlTagInclusion.IncludeTagValue)
  					return settings.IncludeUnknownTags;
  			}
  
  			// This check isn't needed, but it bypasses the StreamLength calculation if its not needed
  			if (settings.IncludeLargeTags == StudyXmlTagInclusion.IncludeTagValue)
  				return settings.IncludeLargeTags;
  
  			// check the size
  			ulong length = attribute.StreamLength;
  			if (length <= settings.MaxTagLength)
  				return StudyXmlTagInclusion.IncludeTagValue;
  
  			// Move here, such that we first check if the tag should be excluded
  			if ((attribute is DicomAttributeOB)
  			 || (attribute is DicomAttributeOW)
  			 || (attribute is DicomAttributeOF)
  			 || (attribute is DicomAttributeOD)
  			 || (attribute is DicomFragmentSequence))
  				return StudyXmlTagInclusion.IncludeTagExclusion;
  
  			return settings.IncludeLargeTags;
  		}
  
  		private void ParseTo(uint tag)
  		{
  			while (_baseCollectionEnumerator != null && _baseCollectionEnumerator.Current.Tag.TagValue <= tag)
  			{
  				_collection[_baseCollectionEnumerator.Current.Tag] = _baseCollectionEnumerator.Current.Copy();
  				if (!_baseCollectionEnumerator.MoveNext())
  					_baseCollectionEnumerator = null;
  			}
  
  			while (_instanceXmlEnumerator != null)
  			{
  				XmlNode node = (XmlNode)_instanceXmlEnumerator.Current;
  				String tagString = node.Attributes["Tag"].Value;
  				uint tagValue;
  				if (tagString.StartsWith("$"))
  				{
  					DicomTag dicomTag = DicomTagDictionary.GetDicomTag(tagString.Substring(1));
  					if (dicomTag == null) throw new DicomDataException("Invalid tag name when parsing XML: " + tagString);
  					tagValue = dicomTag.TagValue;
  				}
  				else
  				{
  					tagValue = uint.Parse(tagString, NumberStyles.HexNumber);
  				}
  
  				if (tagValue <= tag)
  				{
  					ParseAttribute(_collection, node);
  					if (!_instanceXmlEnumerator.MoveNext())
  						_instanceXmlEnumerator = null;
  				}
  				else break;
  			}
  		}
  
  		private static void ParseCollection(DicomAttributeCollection theCollection, XmlNode theNode)
  		{
  			XmlNode attributeNode = theNode.FirstChild;
  			while (attributeNode != null)
  			{
  				ParseAttribute(theCollection, attributeNode);
  				attributeNode = attributeNode.NextSibling;
  			}
  		}
  
  		private static void ParseAttribute(DicomAttributeCollection theCollection, XmlNode attributeNode)
  		{
  			if (attributeNode.Name.Equals("Attribute"))
  			{
  				DicomTag theTag = GetTagFromAttributeNode(attributeNode);
  
  				DicomAttribute attribute = theCollection[theTag];
  				if (attribute is DicomAttributeSQ)
  				{
  					DicomAttributeSQ attribSQ = (DicomAttributeSQ)attribute;
  					//set the null value in case there are no child nodes.
  					attribute.SetNullValue();
  
  					if (attributeNode.HasChildNodes)
  					{
  						XmlNode itemNode = attributeNode.FirstChild;
  
  						while (itemNode != null)
  						{
  
gt: 							DicomSequenceItem theItem = new InstanceXmlDicomSequenceItem();