|
// ==++==
//
// 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
}
}
|