e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
582 lines
21 KiB
C#
582 lines
21 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
/*============================================================
|
|
**
|
|
** Class: CfgParser
|
|
**
|
|
**
|
|
** Purpose: XMLParser and Tree builder internal to BCL
|
|
**
|
|
**
|
|
===========================================================*/
|
|
|
|
namespace System
|
|
{
|
|
using System.Runtime.InteropServices;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Security.Permissions;
|
|
using System.Security;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Runtime.Versioning;
|
|
using System.Diagnostics.Contracts;
|
|
|
|
[Serializable]
|
|
internal enum ConfigEvents
|
|
{
|
|
StartDocument = 0,
|
|
StartDTD = StartDocument + 1,
|
|
EndDTD = StartDTD + 1,
|
|
StartDTDSubset = EndDTD + 1,
|
|
EndDTDSubset = StartDTDSubset + 1,
|
|
EndProlog = EndDTDSubset + 1,
|
|
StartEntity = EndProlog + 1,
|
|
EndEntity = StartEntity + 1,
|
|
EndDocument = EndEntity + 1,
|
|
DataAvailable = EndDocument + 1,
|
|
LastEvent = DataAvailable
|
|
}
|
|
|
|
[Serializable]
|
|
internal enum ConfigNodeType
|
|
{
|
|
Element = 1,
|
|
Attribute = Element + 1,
|
|
Pi = Attribute + 1,
|
|
XmlDecl = Pi + 1,
|
|
DocType = XmlDecl + 1,
|
|
DTDAttribute = DocType + 1,
|
|
EntityDecl = DTDAttribute + 1,
|
|
ElementDecl = EntityDecl + 1,
|
|
AttlistDecl = ElementDecl + 1,
|
|
Notation = AttlistDecl + 1,
|
|
Group = Notation + 1,
|
|
IncludeSect = Group + 1,
|
|
PCData = IncludeSect + 1,
|
|
CData = PCData + 1,
|
|
IgnoreSect = CData + 1,
|
|
Comment = IgnoreSect + 1,
|
|
EntityRef = Comment + 1,
|
|
Whitespace = EntityRef + 1,
|
|
Name = Whitespace + 1,
|
|
NMToken = Name + 1,
|
|
String = NMToken + 1,
|
|
Peref = String + 1,
|
|
Model = Peref + 1,
|
|
ATTDef = Model + 1,
|
|
ATTType = ATTDef + 1,
|
|
ATTPresence = ATTType + 1,
|
|
DTDSubset = ATTPresence + 1,
|
|
LastNodeType = DTDSubset + 1
|
|
}
|
|
|
|
[Serializable]
|
|
internal enum ConfigNodeSubType
|
|
{
|
|
Version = (int)ConfigNodeType.LastNodeType,
|
|
Encoding = Version + 1,
|
|
Standalone = Encoding + 1,
|
|
NS = Standalone + 1,
|
|
XMLSpace = NS + 1,
|
|
XMLLang = XMLSpace + 1,
|
|
System = XMLLang + 1,
|
|
Public = System + 1,
|
|
NData = Public + 1,
|
|
AtCData = NData + 1,
|
|
AtId = AtCData + 1,
|
|
AtIdref = AtId + 1,
|
|
AtIdrefs = AtIdref + 1,
|
|
AtEntity = AtIdrefs + 1,
|
|
AtEntities = AtEntity + 1,
|
|
AtNmToken = AtEntities + 1,
|
|
AtNmTokens = AtNmToken + 1,
|
|
AtNotation = AtNmTokens + 1,
|
|
AtRequired = AtNotation + 1,
|
|
AtImplied = AtRequired + 1,
|
|
AtFixed = AtImplied + 1,
|
|
PentityDecl = AtFixed + 1,
|
|
Empty = PentityDecl + 1,
|
|
Any = Empty + 1,
|
|
Mixed = Any + 1,
|
|
Sequence = Mixed + 1,
|
|
Choice = Sequence + 1,
|
|
Star = Choice + 1,
|
|
Plus = Star + 1,
|
|
Questionmark = Plus + 1,
|
|
LastSubNodeType = Questionmark + 1
|
|
}
|
|
|
|
internal abstract class BaseConfigHandler
|
|
{
|
|
// These delegates must be at the very start of the object
|
|
// This is necessary because unmanaged code takes a dependency on this layout
|
|
// Any changes made to this must be reflected in ConfigHelper.h in ConfigFactory class
|
|
protected Delegate[] eventCallbacks;
|
|
public BaseConfigHandler()
|
|
{
|
|
InitializeCallbacks();
|
|
}
|
|
private void InitializeCallbacks()
|
|
{
|
|
if (eventCallbacks == null)
|
|
{
|
|
eventCallbacks = new Delegate[6];
|
|
eventCallbacks[0] = new NotifyEventCallback(this.NotifyEvent);
|
|
eventCallbacks[1] = new BeginChildrenCallback(this.BeginChildren);
|
|
eventCallbacks[2] = new EndChildrenCallback(this.EndChildren);
|
|
eventCallbacks[3] = new ErrorCallback(this.Error);
|
|
eventCallbacks[4] = new CreateNodeCallback(this.CreateNode);
|
|
eventCallbacks[5] = new CreateAttributeCallback(this.CreateAttribute);
|
|
}
|
|
}
|
|
|
|
private delegate void NotifyEventCallback(ConfigEvents nEvent);
|
|
public abstract void NotifyEvent(ConfigEvents nEvent);
|
|
|
|
private delegate void BeginChildrenCallback(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)] String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
public abstract void BeginChildren(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)] String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
|
|
private delegate void EndChildrenCallback(int fEmpty,
|
|
int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)] String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
public abstract void EndChildren(int fEmpty,
|
|
int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)] String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
|
|
private delegate void ErrorCallback(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
public abstract void Error(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
|
|
private delegate void CreateNodeCallback(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
public abstract void CreateNode(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
|
|
private delegate void CreateAttributeCallback(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
public abstract void CreateAttribute(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength);
|
|
|
|
[System.Security.SecurityCritical] // auto-generated
|
|
[ResourceExposure(ResourceScope.Machine)]
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
internal extern void RunParser(String fileName);
|
|
}
|
|
|
|
// Class used to build a DOM like tree of parsed XML
|
|
internal class ConfigTreeParser : BaseConfigHandler
|
|
{
|
|
ConfigNode rootNode = null;
|
|
ConfigNode currentNode = null;
|
|
String fileName = null;
|
|
int attributeEntry;
|
|
String key = null;
|
|
String [] treeRootPath = null; // element to start tree
|
|
bool parsing = false;
|
|
int depth = 0;
|
|
int pathDepth = 0;
|
|
int searchDepth = 0;
|
|
bool bNoSearchPath = false;
|
|
|
|
// Track state for error message formatting
|
|
String lastProcessed = null;
|
|
bool lastProcessedEndElement;
|
|
|
|
|
|
// NOTE: This parser takes a path eg. /configuration/system.runtime.remoting
|
|
// and will return a node which matches this.
|
|
[ResourceExposure(ResourceScope.Machine)]
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
internal ConfigNode Parse(String fileName, String configPath)
|
|
{
|
|
return Parse(fileName, configPath, false);
|
|
}
|
|
|
|
[System.Security.SecuritySafeCritical] // auto-generated
|
|
[ResourceExposure(ResourceScope.Machine)]
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
internal ConfigNode Parse(String fileName, String configPath, bool skipSecurityStuff)
|
|
{
|
|
if (fileName == null)
|
|
throw new ArgumentNullException("fileName");
|
|
Contract.EndContractBlock();
|
|
this.fileName = fileName;
|
|
if (configPath[0] == '/'){
|
|
treeRootPath = configPath.Substring(1).Split('/');
|
|
pathDepth = treeRootPath.Length - 1;
|
|
bNoSearchPath = false;
|
|
}
|
|
else{
|
|
treeRootPath = new String[1];
|
|
treeRootPath[0] = configPath;
|
|
bNoSearchPath = true;
|
|
}
|
|
|
|
if (!skipSecurityStuff) {
|
|
(new FileIOPermission( FileIOPermissionAccess.Read, System.IO.Path.GetFullPathInternal( fileName ) )).Demand();
|
|
}
|
|
#pragma warning disable 618
|
|
(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert();
|
|
#pragma warning restore 618
|
|
|
|
try
|
|
{
|
|
RunParser(fileName);
|
|
}
|
|
catch(FileNotFoundException) {
|
|
throw; // Pass these through unadulterated.
|
|
}
|
|
catch(DirectoryNotFoundException) {
|
|
throw; // Pass these through unadulterated.
|
|
}
|
|
catch(UnauthorizedAccessException) {
|
|
throw;
|
|
}
|
|
catch(FileLoadException) {
|
|
throw;
|
|
}
|
|
catch(Exception inner) {
|
|
String message = GetInvalidSyntaxMessage();
|
|
// Neither Exception nor ApplicationException are the "right" exceptions here.
|
|
// Desktop throws ApplicationException for backwards compatibility.
|
|
// On Silverlight we don't have ApplicationException, so fall back to Exception.
|
|
#if FEATURE_CORECLR
|
|
throw new Exception(message, inner);
|
|
#else
|
|
throw new ApplicationException(message, inner);
|
|
#endif
|
|
}
|
|
return rootNode;
|
|
}
|
|
|
|
public override void NotifyEvent(ConfigEvents nEvent)
|
|
{
|
|
BCLDebug.Trace("REMOTE", "NotifyEvent "+((Enum)nEvent).ToString()+"\n");
|
|
}
|
|
|
|
public override void BeginChildren(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)] String text,
|
|
int textLength,
|
|
int prefixLength)
|
|
{
|
|
//Trace("BeginChildren",size,subType,nType,terminal,text,textLength,prefixLength,0);
|
|
if (!parsing &&
|
|
(!bNoSearchPath
|
|
&& depth == (searchDepth + 1)
|
|
&& String.Compare(text, treeRootPath[searchDepth], StringComparison.Ordinal) == 0))
|
|
{
|
|
searchDepth++;
|
|
}
|
|
}
|
|
|
|
public override void EndChildren(int fEmpty,
|
|
int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)] String text,
|
|
int textLength,
|
|
int prefixLength)
|
|
{
|
|
lastProcessed = text;
|
|
lastProcessedEndElement = true;
|
|
if (parsing)
|
|
{
|
|
//Trace("EndChildren",size,subType,nType,terminal,text,textLength,prefixLength,fEmpty);
|
|
|
|
if (currentNode == rootNode)
|
|
{
|
|
// End of section of tree which is parsed
|
|
parsing = false;
|
|
}
|
|
|
|
currentNode = currentNode.Parent;
|
|
}
|
|
else if (nType == ConfigNodeType.Element){
|
|
if(depth == searchDepth && String.Compare(text, treeRootPath[searchDepth - 1], StringComparison.Ordinal) == 0)
|
|
{
|
|
searchDepth--;
|
|
depth--;
|
|
}
|
|
else
|
|
depth--;
|
|
}
|
|
}
|
|
|
|
public override void Error(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength)
|
|
{
|
|
//Trace("Error",size,subType,nType,terminal,text,textLength,prefixLength,0);
|
|
}
|
|
|
|
public override void CreateNode(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength)
|
|
{
|
|
//Trace("CreateNode",size,subType,nType,terminal,text,textLength,prefixLength,0);
|
|
|
|
if (nType == ConfigNodeType.Element)
|
|
{
|
|
// New Node
|
|
lastProcessed = text;
|
|
lastProcessedEndElement = false;
|
|
|
|
if (parsing
|
|
|| (bNoSearchPath &&
|
|
String.Compare(text, treeRootPath[0], StringComparison.OrdinalIgnoreCase) == 0)
|
|
|| (depth == searchDepth && searchDepth == pathDepth &&
|
|
String.Compare(text, treeRootPath[pathDepth], StringComparison.OrdinalIgnoreCase) == 0 ))
|
|
{
|
|
parsing = true;
|
|
|
|
ConfigNode parentNode = currentNode;
|
|
currentNode = new ConfigNode(text, parentNode);
|
|
if (rootNode == null)
|
|
rootNode = currentNode;
|
|
else
|
|
parentNode.AddChild(currentNode);
|
|
}
|
|
else
|
|
depth++;
|
|
}
|
|
else if (nType == ConfigNodeType.PCData)
|
|
{
|
|
// Data node
|
|
if (currentNode != null)
|
|
{
|
|
currentNode.Value = text;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void CreateAttribute(int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength)
|
|
{
|
|
//Trace("CreateAttribute",size,subType,nType,terminal,text,textLength,prefixLength,0);
|
|
if (parsing)
|
|
{
|
|
// if the value of the attribute is null, the parser doesn't come back, so need to store the attribute when the
|
|
// attribute name is encountered
|
|
if (nType == ConfigNodeType.Attribute)
|
|
{
|
|
attributeEntry = currentNode.AddAttribute(text, "");
|
|
key = text;
|
|
}
|
|
else if (nType == ConfigNodeType.PCData)
|
|
{
|
|
currentNode.ReplaceAttribute(attributeEntry, key, text);
|
|
}
|
|
else
|
|
{
|
|
String message = GetInvalidSyntaxMessage();
|
|
// Neither Exception nor ApplicationException are the "right" exceptions here.
|
|
// Desktop throws ApplicationException for backwards compatibility.
|
|
// On Silverlight we don't have ApplicationException, so fall back to Exception.
|
|
#if FEATURE_CORECLR
|
|
throw new Exception(message);
|
|
#else
|
|
throw new ApplicationException(message);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#if _DEBUG
|
|
[System.Diagnostics.Conditional("_LOGGING")]
|
|
private void Trace(String name,
|
|
int size,
|
|
ConfigNodeSubType subType,
|
|
ConfigNodeType nType,
|
|
int terminal,
|
|
[MarshalAs(UnmanagedType.LPWStr)]String text,
|
|
int textLength,
|
|
int prefixLength, int fEmpty)
|
|
{
|
|
|
|
BCLDebug.Trace("REMOTE","Node "+name);
|
|
BCLDebug.Trace("REMOTE","text "+text);
|
|
BCLDebug.Trace("REMOTE","textLength "+textLength);
|
|
BCLDebug.Trace("REMOTE","size "+size);
|
|
BCLDebug.Trace("REMOTE","subType "+((Enum)subType).ToString());
|
|
BCLDebug.Trace("REMOTE","nType "+((Enum)nType).ToString());
|
|
BCLDebug.Trace("REMOTE","terminal "+terminal);
|
|
BCLDebug.Trace("REMOTE","prefixLength "+prefixLength);
|
|
BCLDebug.Trace("REMOTE","fEmpty "+fEmpty+"\n");
|
|
}
|
|
#endif
|
|
|
|
private String GetInvalidSyntaxMessage()
|
|
{
|
|
String lastProcessedTag = null;
|
|
|
|
if (lastProcessed != null)
|
|
lastProcessedTag = (lastProcessedEndElement ? "</" : "<") + lastProcessed + ">";
|
|
|
|
return Environment.GetResourceString("XML_Syntax_InvalidSyntaxInFile", fileName, lastProcessedTag);
|
|
}
|
|
}
|
|
|
|
// Node in Tree produced by ConfigTreeParser
|
|
internal class ConfigNode
|
|
{
|
|
String m_name = null;
|
|
String m_value = null;
|
|
ConfigNode m_parent = null;
|
|
List<ConfigNode> m_children = new List<ConfigNode>(5);
|
|
List<DictionaryEntry> m_attributes = new List<DictionaryEntry>(5);
|
|
|
|
internal ConfigNode(String name, ConfigNode parent)
|
|
{
|
|
m_name = name;
|
|
m_parent = parent;
|
|
}
|
|
|
|
internal String Name
|
|
{
|
|
get {return m_name;}
|
|
}
|
|
|
|
internal String Value
|
|
{
|
|
get {return m_value;}
|
|
set {m_value = value;}
|
|
}
|
|
|
|
internal ConfigNode Parent
|
|
{
|
|
get {return m_parent;}
|
|
}
|
|
|
|
internal List<ConfigNode> Children
|
|
{
|
|
get {return m_children;}
|
|
}
|
|
|
|
internal List<DictionaryEntry> Attributes
|
|
{
|
|
get {return m_attributes;}
|
|
}
|
|
|
|
internal void AddChild(ConfigNode child)
|
|
{
|
|
child.m_parent = this;
|
|
m_children.Add(child);
|
|
}
|
|
|
|
internal int AddAttribute(String key, String value)
|
|
{
|
|
m_attributes.Add(new DictionaryEntry(key, value));
|
|
return m_attributes.Count-1;
|
|
}
|
|
|
|
internal void ReplaceAttribute(int index, String key, String value)
|
|
{
|
|
m_attributes[index] = new DictionaryEntry(key, value);
|
|
}
|
|
|
|
#if _DEBUG
|
|
[System.Diagnostics.Conditional("_LOGGING")]
|
|
internal void Trace()
|
|
{
|
|
BCLDebug.Trace("REMOTE","************ConfigNode************");
|
|
BCLDebug.Trace("REMOTE","Name = "+m_name);
|
|
if (m_value != null)
|
|
BCLDebug.Trace("REMOTE","Value = "+m_value);
|
|
if (m_parent != null)
|
|
BCLDebug.Trace("REMOTE","Parent = "+m_parent.Name);
|
|
for (int i=0; i<m_attributes.Count; i++)
|
|
{
|
|
DictionaryEntry de = (DictionaryEntry)m_attributes[i];
|
|
BCLDebug.Trace("REMOTE","Key = "+de.Key+" Value = "+de.Value);
|
|
}
|
|
|
|
for (int i=0; i<m_children.Count; i++)
|
|
{
|
|
((ConfigNode)m_children[i]).Trace();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|