blob: 429377343418a5c80fc7fd2e4d6c513b6f4ab81b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
using PortCMIS.Binding.AtomPub;
using PortCMIS.Data.Extensions;
using PortCMIS.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using PortCMIS.Enums;
using System.Globalization;
namespace PortCMIS.Binding.AtomPub
{
internal abstract class XmlWalker<T>
{
public T Walk(XmlReader parser)
{
T result = PrepareTarget(parser, parser.LocalName, parser.NamespaceURI);
if (!parser.IsEmptyElement)
{
XmlUtils.Next(parser);
// walk through all tags
while (true)
{
XmlNodeType nodeType = parser.NodeType;
if (nodeType == XmlNodeType.Element)
{
if (!Read(parser, parser.LocalName, parser.NamespaceURI, result))
{
if (result is IExtensionsData)
{
HandleExtension(parser, (IExtensionsData)result);
}
else
{
XmlUtils.Skip(parser);
}
}
}
else if (nodeType == XmlNodeType.EndElement)
{
break;
}
else
{
if (!XmlUtils.Next(parser))
{
break;
}
}
}
}
XmlUtils.Next(parser);
return result;
}
protected bool IsCmisNamespace(string ns)
{
return XmlConstants.NamespaceCmis == ns;
}
protected bool IsAtomNamespace(string ns)
{
return XmlConstants.NamespaceAtom == ns;
}
protected bool IsTag(string name, string tag)
{
return name == tag;
}
protected void HandleExtension(XmlReader parser, IExtensionsData extData)
{
IList<ICmisExtensionElement> extensions = extData.Extensions;
if (extensions == null)
{
extensions = new List<ICmisExtensionElement>();
extData.Extensions = extensions;
}
if (extensions.Count + 1 > XmlConstraints.MaxExtensionsWidth)
{
throw new CmisInvalidArgumentException("Too many extensions! (More than " + XmlConstraints.MaxExtensionsWidth + " extensions.)");
}
extensions.Add(HandleExtensionLevel(parser, 0));
}
private ICmisExtensionElement HandleExtensionLevel(XmlReader parser, int level)
{
string localname = parser.Name;
string ns = parser.NamespaceURI;
IDictionary<string, string> attributes = null;
StringBuilder sb = new StringBuilder();
IList<ICmisExtensionElement> children = null;
if (parser.HasAttributes)
{
attributes = new Dictionary<string, string>();
while (parser.MoveToNextAttribute())
{
attributes.Add(parser.Name, parser.Value);
}
}
XmlUtils.Next(parser);
while (true)
{
XmlNodeType nodeType = parser.NodeType;
if (nodeType == XmlNodeType.EndElement)
{
break;
}
else if (nodeType == XmlNodeType.Text)
{
string s = parser.Value;
if (s != null)
{
if (sb.Length + s.Length > XmlConstraints.MaxStringLength)
{
throw new CmisInvalidArgumentException("String limit exceeded! (String is longer than " + XmlConstraints.MaxStringLength + " characters.)");
}
sb.Append(s);
}
}
else if (nodeType == XmlNodeType.Element)
{
if (level + 1 > XmlConstraints.MaxExtensionsDepth)
{
throw new CmisInvalidArgumentException("Extensions tree too deep! (More than " + XmlConstraints.MaxExtensionsDepth + " levels.)");
}
if (children == null)
{
children = new List<ICmisExtensionElement>();
}
if (children.Count + 1 > XmlConstraints.MaxExtensionsWidth)
{
throw new CmisInvalidArgumentException("Extensions tree too wide! (More than " + XmlConstraints.MaxExtensionsWidth + " extensions on one level.)");
}
children.Add(HandleExtensionLevel(parser, level + 1));
continue;
}
if (!XmlUtils.Next(parser))
{
break;
}
}
XmlUtils.Next(parser);
CmisExtensionElement element = new CmisExtensionElement()
{
Name = localname,
Namespace = ns,
Attributes = attributes
};
if (children != null)
{
element.Children = children;
}
else
{
element.Value = sb.ToString();
}
return element;
}
protected IList<S> AddToList<S>(IList<S> list, S value)
{
if (list == null)
{
list = new List<S>();
}
list.Add(value);
return list;
}
protected string ReadText(XmlReader parser)
{
return XmlUtils.ReadText(parser, XmlConstraints.MaxStringLength);
}
protected bool? ReadBoolean(XmlReader parser)
{
string value = ReadText(parser);
if (value == "true" || value == "1")
{
return true;
}
if (value == "false" || value == "0")
{
return false;
}
throw new CmisInvalidArgumentException("Invalid boolean value!");
}
protected BigInteger ReadInteger(XmlReader parser)
{
string value = ReadText(parser);
try
{
return BigInteger.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
catch (Exception e)
{
throw new CmisInvalidArgumentException("Invalid integer value!", e);
}
}
protected decimal ReadDecimal(XmlReader parser)
{
string value = ReadText(parser);
try
{
return Decimal.Parse(value);
}
catch (Exception e)
{
throw new CmisInvalidArgumentException("Invalid decimal value!", e);
}
}
protected DateTime ReadDateTime(XmlReader parser)
{
string value = ReadText(parser);
DateTime result = DateTimeHelper.ParseISO8601(value);
if (result == null)
{
throw new CmisInvalidArgumentException("Invalid datetime value!");
}
return result;
}
protected E ReadEnum<E>(XmlReader parser)
{
return ReadText(parser).GetCmisEnum<E>();
}
protected abstract T PrepareTarget(XmlReader parser, string localname, string ns);
protected abstract bool Read(XmlReader parser, string localname, string ns, T target);
}
internal class XmlConstraints
{
public const int MaxStringLength = 100 * 1024;
public const int MaxExtensionsWidth = 1000;
public const int MaxExtensionsDepth = 100;
}
}