{*********************************************************} {* VPXPARSR.PAS 1.03 *} {*********************************************************} {* ***** BEGIN LICENSE BLOCK ***** *} {* Version: MPL 1.1 *} {* *} {* The contents of this file are subject to the Mozilla Public License *} {* Version 1.1 (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.mozilla.org/MPL/ *} {* *} {* Software distributed under the License is distributed on an "AS IS" basis, *} {* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License *} {* for the specific language governing rights and limitations under the *} {* License. *} {* *} {* The Original Code is TurboPower Visual PlanIt *} {* *} {* The Initial Developer of the Original Code is TurboPower Software *} {* *} {* Portions created by TurboPower Software Inc. are Copyright (C) 2002 *} {* TurboPower Software Inc. All Rights Reserved. *} {* *} {* Contributor(s): *} {* *} {* ***** END LICENSE BLOCK ***** *} {$I Vp.INC} unit VpXParsr; interface uses {$IFDEF LCL} LMessages,LCLProc,LCLType, {$ELSE} Windows, {$ENDIF} Graphics, Controls, SysUtils, Classes, VpConst, VpSR, VpBase, VpXBase, VpXChrFlt; type StringIds = array[0..1] of DOMString; {== Event types ======================================================} TVpDocTypeDeclEvent = procedure(oOwner : TObject; sDecl, sId0, sId1 : DOMString) of object; TVpValueEvent = procedure(oOwner : TObject; sValue : DOMString) of object; TVpAttributeEvent = procedure(oOwner : TObject; sName, sValue : DOMString; bSpecified : Boolean) of object; TVpProcessInstrEvent = procedure(oOwner : TObject; sName, sValue : DOMString) of object; TVpResolveEvent = procedure(oOwner : TObject; const sName, sPublicId, sSystemId : DOMString; var sValue : DOMString) of object; TVpNonXMLEntityEvent = procedure(oOwner : TObject; sEntityName, sPublicId, sSystemId, sNotationName : DOMString) of object; TVpPreserveSpaceEvent = procedure(oOwner : TObject; sElementName : DOMString; var bPreserve : Boolean) of object; {== Class types ======================================================} TVpParser = class(TVpComponent) protected { Private declarations } FAttrEnum : TStringList; FAttributeType : TStringList; FBufferSize : Integer; FCDATA : Boolean; FContext : Integer; FCurrentElement : DOMString; FCurrentElementContent : Integer; FCurrentPath : string; FDataBuffer : DOMString; FDocStack : TList; FElementInfo : TStringList; FEntityInfo : TStringList; FErrors : TStringList; FFilter : TVpInCharFilter; FInCharSet : TVpCharEncoding; FNormalizeData : Boolean; FNotationInfo : TStringList; FOnAttribute : TVpAttributeEvent; FOnCDATASection : TVpValueEvent; FOnCharData : TVpValueEvent; FOnComment : TVpValueEvent; FOnDocTypeDecl : TVpDocTypeDeclEvent; FOnEndDocument : TNotifyEvent; FOnEndElement : TVpValueEvent; FOnIgnorableWhitespace : TVpValueEvent; FOnNonXMLEntity : TVpNonXMLEntityEvent; FOnPreserveSpace : TVpPreserveSpaceEvent; FOnProcessingInstruction : TVpProcessInstrEvent; FOnResolveEntity : TVpResolveEvent; FOnStartDocument : TNotifyEvent; FOnStartElement : TVpValueEvent; FOnBeginElement : TVpValueEvent; FPreserve : Boolean; FRaiseErrors : Boolean; FTagAttributes : TStringList; FTempFiles : TStringList; FUrl : DOMString; FIsStandAlone : Boolean; FHasExternals : Boolean; FXMLDecParsed : Boolean; procedure Cleanup; procedure CheckParamEntityNesting(const aString : DOMString); procedure DataBufferAppend(const sVal : DOMString); procedure DataBufferFlush; procedure DataBufferNormalize; function DataBufferToString : DOMString; function DeclaredAttributes(const sName : DOMString; aIdx : Integer) : TStringList; function GetAttributeDefaultValueType(const sElemName, sAttrName : DOMString) : Integer; function GetAttributeExpandedValue(const sElemName, sAttrName : DOMString; aIdx : Integer) : DOMString; function GetElementContentType(const sName : DOMString; aIdx : Integer) : Integer; function GetElementIndexOf(const sElemName : DOMString) : Integer; function GetEntityIndexOf(const sEntityName : DOMString; aPEAllowed : Boolean) : Integer; function GetEntityNotationName(const sEntityName : DOMString) : DOMString; function GetEntityPublicId(const sEntityName : DOMString) : DOMString; function GetEntitySystemId(const sEntityName : DOMString) : DOMString; function GetEntityType(const sEntityName : DOMString; aPEAllowed : Boolean) : Integer; function GetEntityValue(const sEntityName : DOMString; aPEAllowed : Boolean) : DOMString; function GetErrorCount : Integer; function GetExternalTextEntityValue(const sName, sPublicId : DOMString; sSystemId : DOMString) : DOMString; function GetInCharSet : TVpCharEncoding; procedure Initialize; function IsEndDocument : Boolean; function IsWhitespace(const cVal : DOMChar) : Boolean; function LoadDataSource(sSrcName : string; oErrors : TStringList) : Boolean; function ParseAttribute(const sName : DOMString) : DOMString; function ParseEntityRef(bPEAllowed : Boolean) : DOMString; procedure ParseCDSect; function ParseCharRef : DOMChar; procedure ParseComment; procedure ParseContent; procedure ParseDocTypeDecl; procedure ParseDocument; procedure ParseEndTag; procedure ParseEq; procedure ParseElement; procedure ParseMisc; function ParseParameterEntityRef(aPEAllowed : Boolean; bSkip : Boolean) : DOMString; procedure ParsePCData(aInEntityRef : Boolean); procedure ParsePI; function ParsePIEx : Boolean; { Returns true if an XML declaration was found } procedure ParsePrim; procedure ParseProlog; procedure ParseUntil(const S : array of Longint); procedure ParseWhitespace; procedure ParseXMLDeclaration; procedure PopDocument; procedure PushDocument; procedure PushString(const sVal : DOMString); function ReadChar(const UpdatePos : Boolean) : DOMChar; procedure ReadExternalIds(bInNotation : Boolean; var sIds : StringIds); function ReadLiteral(wFlags : Integer; var HasEntRef : Boolean) : DOMString; function ReadNameToken(aValFirst : Boolean) : DOMString; procedure Require(const S : array of Longint); procedure RequireWhitespace; procedure SetAttribute(const sElemName, sName : DOMString; wType : Integer; const sEnum, sValue : DOMString; wValueType : Integer); procedure SetElement(const sName : DOMString; wType : Integer; const sContentModel : DOMString); procedure SetEntity(const sEntityName : DOMString; wClass : Integer; const sPublicId, sSystemId, sValue, sNotationName : DOMString; aIsPE : Boolean); procedure SetInternalEntity(const sName, sValue : DOMString; aIsPE : Boolean); procedure SetNotation(const sNotationName, sPublicId, sSystemId : DOMString); procedure SkipChar; procedure SkipWhitespace(aNextDoc : Boolean); function TryRead(const S : array of Longint) : Boolean; procedure ValidateAttribute(const aValue : DOMString; HasEntRef : Boolean); procedure ValidateCData(const CDATA : DOMString); procedure ValidateElementName(const aName : DOMString); procedure ValidateEncName(const aValue : string); procedure ValidateEntityValue(const aValue : DOMString; aQuoteCh : DOMChar); function ValidateNameChar(const First : Boolean; const Char : DOMChar) : Boolean; procedure ValidatePCData(const aString : DOMString; aInEntityRef : Boolean); procedure ValidatePublicID(const aString : DOMString); procedure ValidateVersNum(const aString : string); protected { Protected declarations } property OnIgnorableWhitespace : TVpValueEvent read FOnIgnorableWhitespace write FOnIgnorableWhitespace; public { Public declarations } constructor Create(oOwner : TComponent); override; destructor Destroy; override; function GetErrorMsg(wIdx : Integer) : DOMString; function ParseDataSource(const sSource : string) : Boolean; property ErrorCount : Integer read GetErrorCount; property Errors : TStringList read FErrors; property InCharSet : TVpCharEncoding read GetInCharSet; property IsStandAlone : Boolean read FIsStandAlone; property HasExternals : Boolean read FHasExternals; { Published declarations } property BufferSize : Integer read FBufferSize write FBufferSize default 8192; property NormalizeData : Boolean read FNormalizeData write FNormalizeData default True; property RaiseErrors : Boolean read FRaiseErrors write FRaiseErrors default False; property OnAttribute : TVpAttributeEvent read FOnAttribute write FOnAttribute; property OnCDATASection : TVpValueEvent read FOnCDATASection write FOnCDATASection; property OnCharData : TVpValueEvent read FOnCharData write FOnCharData; property OnComment : TVpValueEvent read FOnComment write FOnComment; property OnDocTypeDecl : TVpDocTypeDeclEvent read FOnDocTypeDecl write FOnDocTypeDecl; property OnEndDocument : TNotifyEvent read FOnEndDocument write FOnEndDocument; property OnEndElement : TVpValueEvent read FOnEndElement write FOnEndElement; property OnNonXMLEntity : TVpNonXMLEntityEvent read FOnNonXMLEntity write FOnNonXMLEntity; property OnPreserveSpace : TVpPreserveSpaceEvent read FOnPreserveSpace write FOnPreserveSpace; property OnProcessingInstruction : TVpProcessInstrEvent read FOnProcessingInstruction write FOnProcessingInstruction; property OnResolveEntity : TVpResolveEvent read FOnResolveEntity write FOnResolveEntity; property OnStartDocument : TNotifyEvent read FOnStartDocument write FOnStartDocument; property OnStartElement : TVpValueEvent read FOnStartElement write FOnStartElement; property OnBeginElement : TVpValueEvent read FOnBeginElement write FOnBeginElement; end; implementation {.$R *.RES} {== TVpEntityInfo ====================================================} type TVpEntityInfo = class(TObject) private FEntityClass : Integer; FIsPE : Boolean; FPublicId : DOMString; FSystemId : DOMString; FValue : DOMString; FNotationName : DOMString; public property EntityClass : Integer read FEntityClass write FEntityClass; property IsParameterEntity : Boolean read FIsPE write FIsPE; property NotationName : DOMString read FNotationName write FNotationName; property PublicId : DOMString read FPublicId write FPublicId; property SystemId : DOMString read FSystemId write FSystemId; property Value : DOMString read FValue write FValue; end; {== TVpNotationInfo ==================================================} TVpNotationInfo = class(TObject) private FPublicId : DOMString; FSystemId : DOMString; public property PublicId : DOMString read FPublicId write FPublicId; property SystemId : DOMString read FSystemId write FSystemId; end; {== TVpAttributeInfo =================================================} TVpAttributeInfo = class(TObject) private FType : Integer; FValue : DOMString; FValueType : Integer; FEnum : DOMString; FLookup : DOMString; public property AttrType : Integer read FType write FType; property Enum : DOMString read FEnum write FEnum; property Lookup : DOMString read FLookup write FLookup; property Value : DOMString read FValue write FValue; property ValueType : Integer read FValueType write FValueType; end; {== TVpElementInfo ===================================================} TVpElementInfo = class(TObject) private FAttributeList : TStringList; FContentType : Integer; FContentModel : DOMString; public constructor Create; destructor Destroy; override; procedure SetAttribute(const sName : DOMString; oAttrInfo : TVpAttributeInfo); property AttributeList : TStringList read FAttributeList; property ContentModel : DOMString read FContentModel write FContentModel; property ContentType : Integer read FContentType write FContentType; end; {=== TVpElementInfo ==================================================} constructor TVpElementInfo.Create; begin inherited Create; FAttributeList := nil; FContentModel := ''; FContentType := 0; end; {--------} destructor TVpElementInfo.Destroy; var i : Integer; begin if FAttributeList <> nil then begin for i := 0 to FAttributeList.Count - 1 do TVpAttributeInfo(FAttributeList.Objects[i]).Free; FAttributeList.Free; end; inherited Destroy; end; {--------} procedure TVpElementInfo.SetAttribute(const sName : DOMString; oAttrInfo : TVpAttributeInfo); var wIdx : Integer; begin if FAttributeList = nil then begin FAttributeList := TStringList.Create; FAttributeList.Sorted := True; wIdx := -1 end else wIdx := FAttributeList.IndexOf(sName); if wIdx < 0 then FAttributeList.AddObject(sName, oAttrInfo) else begin TVpAttributeInfo(FAttributeList.Objects[wIdx]).Free; FAttributeList.Objects[wIdx] := oAttrInfo; end; end; {=== TVpParser =======================================================} constructor TVpParser.Create(oOwner : TComponent); begin inherited Create(oOwner); FErrors := TStringList.Create; FAttributeType := TStringList.Create; FAttributeType.AddObject('CDATA', Pointer(ATTRIBUTE_CDATA)); FAttributeType.AddObject('ID', Pointer(ATTRIBUTE_ID)); FAttributeType.AddObject('IDREF', Pointer(ATTRIBUTE_IDREF)); FAttributeType.AddObject('IDREFS', Pointer(ATTRIBUTE_IDREFS)); FAttributeType.AddObject('ENTITY', Pointer(ATTRIBUTE_ENTITY)); FAttributeType.AddObject('ENTITIES', Pointer(ATTRIBUTE_ENTITIES)); FAttributeType.AddObject('NMTOKEN', Pointer(ATTRIBUTE_NMTOKEN)); FAttributeType.AddObject('NMTOKENS', Pointer(ATTRIBUTE_NMTOKENS)); FAttributeType.AddObject('NOTATION', Pointer(ATTRIBUTE_NOTATION)); FElementInfo := TStringList.Create; FElementInfo.Sorted := True; FEntityInfo := TStringList.Create; FInCharSet := ceUnknown; FNotationInfo := TStringList.Create; FNotationInfo.Sorted := true; FNotationInfo.Duplicates := dupIgnore; FTagAttributes := TStringList.Create; FAttrEnum := TStringList.Create; FDocStack := TList.Create; FNormalizeData := True; FCDATA := False; FPreserve := False; FUrl := ''; FRaiseErrors := False; FFilter := nil; FBufferSize := 8192; FCurrentPath := ''; FTempFiles := TStringList.Create; FIsStandAlone := False; FHasExternals := False; FXMLDecParsed := False; end; {--------} destructor TVpParser.Destroy; var TempFilter : TVpInCharFilter; i : Integer; begin Cleanup; FTagAttributes.Free; FNotationInfo.Free; FEntityInfo.Free; FElementInfo.Free; FAttributeType.Free; FErrors.Free; if Assigned(FTempFiles) then begin for i := 0 to Pred(FTempFiles.Count) do DeleteFile(FTempFiles[i]); FTempFiles.Free; end; FAttrEnum.Free; if FDocStack.Count > 0 then begin for i := Pred(FDocStack.Count) to 0 do begin TempFilter := FDocStack[i]; TempFilter.Free; FDocStack.Delete(i); end; end; FDocStack.Free; inherited Destroy; end; {--------} procedure TVpParser.CheckParamEntityNesting(const aString : DOMString); var OpenPos : Integer; ClosePos : Integer; begin OpenPos := VpPos('(', aString); ClosePos := VpPos(')', aString); if (((OpenPos <> 0) and (ClosePos = 0)) or ((ClosePos <> 0) and (OpenPos = 0))) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sBadParamEntNesting + aString); end; {--------} procedure TVpParser.Cleanup; var i : Integer; begin if FElementInfo <> nil then begin for i := 0 to FElementInfo.Count - 1 do TVpElementInfo(FElementInfo.Objects[i]).Free; FElementInfo.Clear; end; if FEntityInfo <> nil then begin for i := 0 to FEntityInfo.Count - 1 do TVpEntityInfo(FEntityInfo.Objects[i]).Free; FEntityInfo.Clear; end; if FNotationInfo <> nil then begin for i := 0 to FNotationInfo.Count - 1 do TVpNotationInfo(FNotationInfo.Objects[i]).Free; FNotationInfo.Clear; end; end; {--------} procedure TVpParser.DataBufferAppend(const sVal : DOMString); begin FDataBuffer := FDataBuffer + sVal; end; {--------} procedure TVpParser.DataBufferFlush; begin if FNormalizeData and not FCDATA and not FPreserve then DataBufferNormalize; if FDataBuffer <> '' then begin case FCurrentElementContent of CONTENT_MIXED, CONTENT_ANY : if FCDATA then begin ValidateCData(FDataBuffer); if Assigned(FOnCDATASection) then FOnCDATASection(self, FDataBuffer); end else begin if Assigned(FOnCharData) then FOnCharData(self, FDataBuffer); end; CONTENT_ELEMENTS : if Assigned(FOnIgnorableWhitespace) then FOnIgnorableWhitespace(self, FDataBuffer); end; FDataBuffer := ''; end; end; {--------} procedure TVpParser.DataBufferNormalize; var BuffLen : Integer; j : Integer; CharDeleted : Boolean; begin while (Length(FDataBuffer) > 0) and IsWhiteSpace(FDataBuffer[1]) do Delete(FDataBuffer, 1, 1); while (Length(FDataBuffer) > 0) and IsWhiteSpace(FDataBuffer[Length(FDataBuffer)]) do Delete(FDataBuffer, Length(FDataBuffer), 1); j := 1; BuffLen := Length(FDataBuffer); CharDeleted := False; while j < BuffLen do begin if IsWhiteSpace(FDataBuffer[j]) then begin { Force whitespace to a single space } FDataBuffer[j] := ' '; { Remove additional whitespace } j := j + 1; while (j <= Length(FDataBuffer)) and IsWhiteSpace(FDataBuffer[j]) do begin Delete(FDataBuffer, j, 1); CharDeleted := True; end; if (CharDeleted) then begin BuffLen := Length(FDataBuffer); CharDeleted := False; end; end; j := j + 1; end; end; {--------} function TVpParser.DataBufferToString : DOMString; begin Result := FDataBuffer; FDataBuffer := ''; end; {--------} function TVpParser.GetErrorCount : Integer; begin Result := FErrors.Count; end; {--------} function TVpParser.GetErrorMsg(wIdx : Integer) : DOMString; begin Result := sIndexOutOfBounds; if (wIdx >= 0) and (wIdx < FErrors.Count) then Result := FErrors[wIdx]; end; {--------} function TVpParser.DeclaredAttributes(const sName : DOMString; aIdx : Integer) : TStringList; begin if aIdx < 0 then Result := nil else Result := TVpElementInfo(FElementInfo.Objects[aIdx]).AttributeList; end; {--------} function TVpParser.GetAttributeDefaultValueType(const sElemName, sAttrName : DOMString) : Integer; var wIdx : Integer; oAttrList : TStringList; oAttr : TVpAttributeInfo; begin Result := ATTRIBUTE_DEFAULT_UNDECLARED; wIdx := GetElementIndexOf(sElemName); if wIdx >= 0 then begin oAttrList := TVpElementInfo(FElementInfo.Objects[wIdx]).AttributeList; if oAttrList <> nil then begin wIdx := oAttrList.IndexOf(sAttrName); if wIdx >= 0 then begin oAttr := TVpAttributeInfo(oAttrList.Objects[wIdx]); Result := oAttr.AttrType; end; end; end; end; {--------} function TVpParser.GetAttributeExpandedValue(const sElemName, sAttrName : DOMString; aIdx : Integer) : DOMString; var wIdx : Integer; oAttrList : TStringList; oAttr : TVpAttributeInfo; HasEntRef : Boolean; begin SetLength(Result, 0); HasEntRef := False; if aIdx >= 0 then begin oAttrList := TVpElementInfo(FElementInfo.Objects[aIdx]).AttributeList; if oAttrList <> nil then begin wIdx := oAttrList.IndexOf(sAttrName); if wIdx >= 0 then begin oAttr := TVpAttributeInfo(oAttrList.Objects[wIdx]); if (oAttr.Lookup = '') and (oAttr.Value <> '') then begin PushString('"' + oAttr.Value + '"'); oAttr.Lookup := ReadLiteral(LIT_NORMALIZE or LIT_CHAR_REF or LIT_ENTITY_REF, HasEntRef); SkipWhitespace(True); end; Result := oAttr.Lookup; end; end; end; end; {--------} function TVpParser.GetElementContentType(const sName : DOMString; aIdx : Integer) : Integer; begin if aIdx < 0 then Result := CONTENT_UNDECLARED else Result := TVpElementInfo(FElementInfo.Objects[aIdx]).ContentType; end; {--------} function TVpParser.GetElementIndexOf(const sElemName : DOMString) : Integer; begin Result := FElementInfo.IndexOf(sElemName); end; {--------} function TVpParser.GetEntityIndexOf(const sEntityName : DOMString; aPEAllowed : Boolean) : Integer; begin for Result := 0 to FEntityInfo.Count - 1 do if FEntityInfo[Result] = sEntityName then begin if (not aPEAllowed) then begin if (not TVpEntityInfo(FEntityInfo.Objects[Result]).IsParameterEntity) then Exit; end else Exit; end; Result := -1; end; {--------} function TVpParser.GetEntityNotationName(const sEntityName : DOMString) : DOMString; var wIdx : Integer; oEntity : TVpEntityInfo; begin Result := ''; wIdx := GetEntityIndexOf(sEntityName, False); if wIdx >= 0 then begin oEntity := TVpEntityInfo(FEntityInfo.Objects[wIdx]); Result := oEntity.NotationName; end; end; {--------} function TVpParser.GetEntityPublicId(const sEntityName : DOMString) : DOMString; var wIdx : Integer; oEntity : TVpEntityInfo; begin Result := ''; wIdx := GetEntityIndexOf(sEntityName, False); if wIdx >= 0 then begin oEntity := TVpEntityInfo(FEntityInfo.Objects[wIdx]); Result := oEntity.PublicId; end; end; {--------} function TVpParser.GetEntitySystemId(const sEntityName : DOMString) : DOMString; var wIdx : Integer; oEntity : TVpEntityInfo; begin Result := ''; wIdx := GetEntityIndexOf(sEntityName, False); if wIdx >= 0 then begin oEntity := TVpEntityInfo(FEntityInfo.Objects[wIdx]); Result := oEntity.SystemId; end; end; {--------} function TVpParser.GetEntityType(const sEntityName : DOMString; aPEAllowed : Boolean) : Integer; var wIdx : Integer; oEntity : TVpEntityInfo; begin Result := ENTITY_UNDECLARED; wIdx := GetEntityIndexOf(sEntityName, aPEAllowed); if wIdx >= 0 then begin oEntity := TVpEntityInfo(FEntityInfo.Objects[wIdx]); Result := oEntity.EntityClass; end; end; {--------} function TVpParser.GetEntityValue(const sEntityName : DOMString; aPEAllowed : Boolean) : DOMString; var wIdx : Integer; oEntity : TVpEntityInfo; begin Result := ''; wIdx := GetEntityIndexOf(sEntityName, aPEAllowed); if wIdx >= 0 then begin oEntity := TVpEntityInfo(FEntityInfo.Objects[wIdx]); Result := oEntity.Value; end; end; {--------} function TVpParser.GetExternalTextEntityValue(const sName, sPublicId : DOMString; sSystemId : DOMString) : DOMString; var CompletePath : string; begin DataBufferFlush; Result := ''; FHasExternals := True; if Assigned(FOnResolveEntity) then FOnResolveEntity(self, sName, sPublicId, sSystemId, sSystemId); if sSystemId = '' then exit; PushDocument; if (VpPos('/', sSystemID) = 0) and (VpPos('\', sSystemID) = 0) then CompletePath := FCurrentPath + sSystemID else CompletePath := sSystemID; {TODO:: Need to check return value of LoadDataSource? } try LoadDataSource(CompletePath, FErrors); except PopDocument; raise; end; end; {--------} function TVpParser.GetInCharSet : TVpCharEncoding; begin if FFilter <> nil then Result := ceUTF8 else { If no current filter then return last known value. } Result := FInCharSet; end; {--------} procedure TVpParser.Initialize; begin FDataBuffer := ''; SetInternalEntity('amp', '&', False); SetInternalEntity('lt', '<', False); SetInternalEntity('gt', '>', False); SetInternalEntity('apos', ''', False); SetInternalEntity('quot', '"', False); end; {--------} function TVpParser.IsEndDocument : Boolean; var TheStream : TStream; DocCount : Integer; begin DocCount := FDocStack.Count; if (DocCount = 0) then Result := FFilter.Eof else begin Result := False; while FFilter.EOF do begin if (DocCount > 0) then begin TheStream := FFilter.Stream; FFilter.Free; TheStream.Free; end; PopDocument; DocCount := FDocStack.Count; end; end; end; {--------} function TVpParser.IsWhitespace(const cVal : DOMChar) : Boolean; begin Result := (cVal = #$20) or (cVal = #$09) or (cVal = #$0D) or (cVal = #$0A); end; {--------} function TVpParser.LoadDataSource(sSrcName : string; oErrors : TStringList) : Boolean; var aFileStream : TVpFileStream; begin begin { Must be a local or network file. Eliminate file:// prefix. } if StrLIComp(PChar(sSrcName), 'file://', 7) = 0 then Delete(sSrcName, 1, 7); if FileExists(sSrcName) then begin FCurrentPath := ExtractFilePath(sSrcName); {the stream and filter are destroyed after the document is parsed} aFileStream := TVpFileStream.CreateEx(fmOpenRead, sSrcName); aFileStream.Position := 0; Result := True; end else begin oErrors.Add(format(sFileNotFound, [sSrcName])); raise EVpParserError.CreateError (0, 0, format(sFileNotFound, [sSrcName])); end; end; if Result then try aFileStream.Position := 0; FFilter := TVpInCharFilter.Create(aFileStream, FBufferSize); except aFileStream.Free; raise; end; end; {--------} function TVpParser.ParseAttribute(const sName : DOMString) : DOMString; var sAttrName, sValue : DOMString; wType : Integer; HasEntRef : Boolean; begin Result := ''; HasEntRef := False; sAttrName := ReadNameToken(True); wType := GetAttributeDefaultValueType(sName, sAttrName); ParseEq; {we need to validate production 10 - 1st letter in quotes} if (wType = ATTRIBUTE_CDATA) or (wType = ATTRIBUTE_UNDECLARED) then sValue := ReadLiteral(LIT_CHAR_REF or LIT_ENTITY_REF, HasEntRef) else sValue := ReadLiteral(LIT_CHAR_REF or LIT_ENTITY_REF or LIT_NORMALIZE, HasEntRef); if not HasEntRef then ValidateAttribute(sValue, HasEntRef); if Assigned(FOnAttribute) then FOnAttribute(self, sAttrName, sValue, True); FDataBuffer := ''; FTagAttributes.Add(sAttrName); if sAttrName = 'xml:space' then Result := sValue; end; {--------} procedure TVpParser.ParseCDSect; {conditional section} begin ParseUntil(Xpc_ConditionalEnd); end; {--------} function TVpParser.ParseCharRef : DOMChar; var TempChar : DOMChar; Ucs4Chr : TVpUcs4Char; begin Ucs4Chr := 0; if TryRead(Xpc_CharacterRefHex) then begin Ucs4Chr := 0; while True do begin TempChar := ReadChar(True); if (TempChar = '0') or (TempChar = '1') or (TempChar = '2') or (TempChar = '3') or (TempChar = '4') or (TempChar = '5') or (TempChar = '6') or (TempChar = '7') or (TempChar = '8') or (TempChar = '9') or (TempChar = 'A') or (TempChar = 'B') or (TempChar = 'C') or (TempChar = 'D') or (TempChar = 'E') or (TempChar = 'F') or (TempChar = 'a') or (TempChar = 'b') or (TempChar = 'c') or (TempChar = 'd') or (TempChar = 'e') or (TempChar = 'f') then begin Ucs4Chr := Ucs4Chr shl 4; Ucs4Chr := Ucs4Chr + StrToIntDef(TempChar, 0); end else if (TempChar = ';') then Break else raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sIllCharInRef + QuotedStr(TempChar)); end; end else begin while True do begin TempChar := ReadChar(True); if (TempChar = '0') or (TempChar = '1') or (TempChar = '2') or (TempChar = '3') or (TempChar = '4') or (TempChar = '5') or (TempChar = '6') or (TempChar = '7') or (TempChar = '8') or (TempChar = '9') then begin Ucs4Chr := Ucs4Chr * 10; Ucs4Chr := Ucs4Chr + StrToIntDef(TempChar, 0); end else if (TempChar = ';') then Break else raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sIllCharInRef + QuotedStr(TempChar)); end; end; VpUcs4ToWideChar(Ucs4Chr, Result); DataBufferAppend(Result); end; {--------} procedure TVpParser.ParseComment; var TempComment : DOMString; begin ParseUntil(Xpc_CommentEnd); TempComment := DataBufferToString; { Did we find '--' within the comment? } if (TempComment <> '') and ((VpPos('--', TempComment) <> 0) or (TempComment[Length(TempComment)] = '-')) then { Yes. Raise an error. } raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvalidCommentText); if Assigned(FOnComment) then FOnComment(self, TempComment); end; {--------} procedure TVpParser.ParseContent; var TempChar : DOMChar; TempStr : DOMString; EntRefs : TStringList; OldLine : Integer; OldPos : Integer; TempInt : Integer; StackLevel : Integer; LastCharAmp : Boolean; begin LastCharAmp := False; StackLevel := 0; TempChar := #0; EntRefs := nil; while True do begin OldLine := FFilter.Line; OldPos := FFilter.LinePos; case FCurrentElementContent of CONTENT_ANY, CONTENT_MIXED : begin if Assigned(EntRefs) then begin if (FDataBuffer <> '&') or (LastCharAmp) then begin ParsePCData(True); LastCharAmp := False; end; { Reset the last ent ref if we parsed something.} if (FFilter.Line <> OldLine) and (FFilter.LinePos <> OldPos) then begin EntRefs.Free; EntRefs := nil; end; end else ParsePCData(TempChar <> ''); end; CONTENT_ELEMENTS : ParseWhitespace; end; TempChar := ReadChar(False); if IsEndDocument then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sUnexpectedEof); if (TempChar = '&') then begin SkipChar; TempChar := ReadChar(False); if TempChar = '#' then begin SkipChar; TempChar := ParseCharRef; if TempChar = '&' then LastCharAmp := True; if (FCurrentElementContent <> CONTENT_ANY) and (FCurrentElementContent <> CONTENT_MIXED) then PushString(TempChar); end else begin if (not Assigned(EntRefs)) then begin StackLevel := Succ(FDocStack.Count); EntRefs := TStringList.Create; TempStr := ParseEntityRef(False); end else begin {Check for circular references} TempStr := ParseEntityRef(False); StackLevel := FDocStack.Count; TempInt := EntRefs.IndexOf(TempStr); if TempInt <> -1 then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sCircularEntRef + TempStr); end; EntRefs.Add(TempStr); end; if (FCurrentElementContent <> CONTENT_ANY) and (FCurrentElementContent <> CONTENT_MIXED) and (TempChar = '<') then begin DataBufferFlush; ParseElement; end else TempChar := ReadChar(False); end else if (TempChar = '<') then begin EntRefs.Free; EntRefs := nil; SkipChar; TempChar := ReadChar(False); if (TempChar = '!') then begin SkipChar; DataBufferFlush; TempChar := ReadChar(True); if (TempChar = '-') then begin Require(Xpc_Dash); ParseComment; end else if (TempChar = '[') then begin Require(Xpc_CDATAStart); FCDATA := True; ParseCDSect; ValidateCData(FDataBuffer); DataBufferFlush; FCDATA := False; end else raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sExpCommentOrCDATA + '(' + TempChar + ')'); end else if (TempChar = '?') then begin EntRefs.Free; EntRefs := nil; SkipChar; DataBufferFlush; ParsePI; end else if (TempChar = '/') then begin SkipChar; DataBufferFlush; ParseEndTag; Exit; end else begin EntRefs.Free; EntRefs := nil; DataBufferFlush; ParseElement; end; end; {if..else} if (Assigned(EntRefs)) and (FDocStack.Count < StackLevel) then begin EntRefs.Clear; StackLevel := FDocStack.Count; end; end; EntRefs.Free; end; {--------} function TVpParser.ParseDataSource(const sSource : string) : Boolean; begin FErrors.Clear; FIsStandAlone := False; FHasExternals := False; FUrl := sSource; Result := LoadDataSource(sSource, FErrors); if Result then begin FFilter.FreeStream := True; ParsePrim; end else FErrors.Add(sSrcLoadFailed + sSource); FUrl := ''; Result := FErrors.Count = 0; end; {--------} procedure TVpParser.ParseDocTypeDecl; var sDocTypeName : DOMString; sIds : StringIds; begin RequireWhitespace; sDocTypeName := ReadNameToken(True); SkipWhitespace(True); ReadExternalIds(False, sIds); SkipWhitespace(True); // Parse external DTD if sIds[1] <> '' then begin end; if sIds[1] <> '' then begin while True do begin FContext := CONTEXT_DTD; SkipWhitespace(True); FContext := CONTEXT_NONE; if TryRead(Xpc_BracketAngleRight) then Break else begin FContext := CONTEXT_DTD; FContext := CONTEXT_NONE; end; end; end else begin SkipWhitespace(True); Require(Xpc_BracketAngleRight); end; if Assigned(FOnDocTypeDecl) then FOnDocTypeDecl(self, sDocTypeName, sIds[0], sIds[1]); end; {--------} procedure TVpParser.ParseDocument; begin FXMLDecParsed := False; ParseProlog; Require(Xpc_BracketAngleLeft); ParseElement; try ParseMisc; except end; SkipWhiteSpace(True); if (not IsEndDocument) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sDataAfterValDoc); if Assigned(FOnEndDocument) then FOnEndDocument(self); end; {--------} procedure TVpParser.ParseElement; var wOldElementContent, i : Integer; sOldElement : DOMString; sGi, sTmp, sTmp2 : DOMString; oTmpAttrs : TStringList; bOldPreserve : Boolean; TempChar : DOMChar; aList : TStringList; ElemIdx : Integer; begin wOldElementContent := FCurrentElementContent; sOldElement := FCurrentElement; bOldPreserve := FPreserve; FTagAttributes.Clear; sGi := ReadNameToken(True); ValidateElementName(sGi); if Assigned(FOnBeginElement) then FOnBeginElement(self, sGi); FCurrentElement := sGi; ElemIdx := GetElementIndexOf(sGi); FCurrentElementContent := GetElementContentType(sGi, ElemIdx); if FCurrentElementContent = CONTENT_UNDECLARED then FCurrentElementContent := CONTENT_ANY; SkipWhitespace(True); sTmp := ''; TempChar := ReadChar(False); while (TempChar <> '/') and (TempChar <> '>') do begin sTmp2 := ParseAttribute(sGi); if sTmp2 <> '' then sTmp := sTmp2; SkipWhitespace(True); TempChar := ReadChar(False); { check for duplicate attributes } if FTagAttributes.Count > 1 then begin aList := TStringList.Create; try aList.Sorted := True; aList.Duplicates := dupIgnore; aList.Assign(FTagAttributes); if (aList.Count <> FTagAttributes.Count) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sRedefinedAttr); finally aList.Free; end; end; end; oTmpAttrs := DeclaredAttributes(sGi, ElemIdx); if oTmpAttrs <> nil then begin for i := 0 to oTmpAttrs.Count - 1 do begin if FTagAttributes.IndexOf(oTmpAttrs[i]) <> - 1 then Continue; if Assigned(FOnAttribute) then begin sTmp2 := GetAttributeExpandedValue(sGi, oTmpAttrs[i], ElemIdx); if sTmp2 <> '' then FOnAttribute(self, oTmpAttrs[i], sTmp2, False); end; end; end; if sTmp = '' then sTmp := GetAttributeExpandedValue(sGi, 'xml:space', ElemIdx); if sTmp = 'preserve' then FPreserve := True else if sTmp = 'default' then FPreserve := not FNormalizeData; if Assigned(FOnPreserveSpace) then FOnPreserveSpace(self, sGi, FPreserve); TempChar := ReadChar(True); if (TempChar = '>') then begin if Assigned(FOnStartElement) then FOnStartElement(self, sGi); ParseContent; end else if (TempChar = '/') then begin Require(Xpc_BracketAngleRight); if Assigned(FOnStartElement) then FOnStartElement(self, sGi); if Assigned(FOnEndElement) then FOnEndElement(self, sGi); end; FPreserve := bOldPreserve; FCurrentElement := sOldElement; FCurrentElementContent := wOldElementContent; end; {--------} procedure TVpParser.ParseEndTag; var sName : DOMString; begin sName := ReadNameToken(True); if sName <> FCurrentElement then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sMismatchEndTag + 'Start tag = "' + FCurrentElement + '" End tag = "' + sName + '"'); SkipWhitespace(True); Require(Xpc_BracketAngleRight); if Assigned(FOnEndElement) then FOnEndElement(self, FCurrentElement); end; {--------} function TVpParser.ParseEntityRef(bPEAllowed : Boolean) : DOMString; begin Result := ReadNameToken(True); Require(Xpc_GenParsedEntityEnd); case GetEntityType(Result, bPEAllowed) of ENTITY_UNDECLARED : begin raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sUndeclaredEntity + QuotedStr(Result)); end; ENTITY_INTERNAL : PushString(GetEntityValue(Result, False)); ENTITY_TEXT : begin (GetExternalTextEntityValue(Result, GetEntityPublicId(Result), GetEntitySystemId(Result))); end; ENTITY_NDATA : begin FHasExternals := True; if Assigned(FOnNonXMLEntity) then FOnNonXMLEntity(self, Result, GetEntityPublicId(Result), GetEntitySystemId(Result), GetEntityNotationName(Result)); end; end; end; {--------} procedure TVpParser.ParseEq; begin SkipWhitespace(True); Require(Xpc_Equation); SkipWhitespace(True); end; {--------} procedure TVpParser.ParseMisc; var ParsedComment : Boolean; begin ParsedComment := False; while True do begin SkipWhitespace(True); if TryRead(Xpc_ProcessInstrStart) then begin if ParsePIEx and ParsedComment then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sCommentBeforeXMLDecl) else FXMLDecParsed := True; end else if TryRead(Xpc_CommentStart) then begin FXMLDecParsed := True; ParsedComment := True; ParseComment; end else Exit; end; end; {--------} function TVpParser.ParseParameterEntityRef(aPEAllowed : Boolean; bSkip : Boolean) : DOMString; var sName, sValue : DOMString; begin sName := ReadNameToken(True); Require(Xpc_GenParsedEntityEnd); case GetEntityType(sName, aPEAllowed) of ENTITY_UNDECLARED : raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos - 3, sUndeclaredEntity + sName); ENTITY_INTERNAL : begin sValue := GetEntityValue(sName, aPEAllowed); if bSkip then DataBufferAppend(sValue) else PushString(sValue); Result := sValue; end; ENTITY_TEXT : begin sValue := GetExternalTextEntityValue(sName, GetEntityPublicId(sName), GetEntitySystemId(sName)); if bSkip then DataBufferAppend(sValue); Result := sValue; end; ENTITY_NDATA : begin FHasExternals := True; if Assigned(FOnNonXMLEntity) then FOnNonXMLEntity(self, sName, GetEntityPublicId(sName), GetEntitySystemId(sName), GetEntityNotationName(sName)); end; end; end; {--------} procedure TVpParser.ParsePCData(aInEntityRef : Boolean); var TempBuff : DOMString; TempChar : DOMChar; CurrLength : Longint; BuffLength : Longint; Added : Boolean; begin Added := False; CurrLength := 0; BuffLength := 50; SetLength(TempBuff, BuffLength); while True do begin TempChar := ReadChar(False); if (TempChar = '<') or (TempChar = '&') or (FFilter.EOF) then Break else begin if ((CurrLength + 2) > BuffLength) then begin BuffLength := BuffLength * 2; SetLength(TempBuff, BuffLength); end; Move(TempChar, PByteArray(Pointer(TempBuff))[CurrLength], 2); Inc(CurrLength, 2); SkipChar; Added := True; end; end; if Added then begin SetLength(TempBuff, CurrLength div 2); ValidatePCData(TempBuff, aInEntityRef); DataBufferAppend(TempBuff); end; end; {--------} procedure TVpParser.ParsePI; begin ParsePIEx; end; {--------} function TVpParser.ParsePIEx : Boolean; var sName : DOMString; begin Result := False; sName := ReadNameToken(True); if sName <> 'xml' then begin FXMLDecParsed := True; if not TryRead(Xpc_ProcessInstrEnd) then begin RequireWhitespace; ParseUntil(Xpc_ProcessInstrEnd); end; end else begin Result := True; ParseXMLDeclaration; end; if Assigned(FOnProcessingInstruction) then FOnProcessingInstruction(self, sName, DataBufferToString) else DataBufferToString; end; {--------} procedure TVpParser.ParsePrim; begin try Initialize; if Assigned(FOnStartDocument) then FOnStartDocument(self); try ParseDocument; except on E: EVpFilterError do begin FErrors.Add(Format(sFmtErrorMsg, [E.Line, E.LinePos, E.Message])); if FRaiseErrors then begin if Assigned(FOnEndDocument) then FOnEndDocument(self); Cleanup; raise; end; end; end; if Assigned(FOnEndDocument) then FOnEndDocument(self); Cleanup; finally FInCharSet := ceUTF8; FFilter.Free; FFilter := nil; end; end; {--------} procedure TVpParser.ParseProlog; begin ParseMisc; if TryRead(Xpc_DTDDocType) then begin FXMLDecParsed := True; ParseDocTypeDecl; ParseMisc; end; end; {--------} procedure TVpParser.ParseUntil(const S : array of Longint); var TempStr : AnsiString; TempChar : AnsiChar; i : Integer; Found : Boolean; begin Found := TryRead(s); while (not Found) and (not FFilter.EOF) do begin DataBufferAppend(ReadChar(True)); Found := TryRead(s); end; if (not Found) then begin {$IFDEF DCC4OrLater} SetLength(TempStr, Length(S)); {$ENDIF} for i := 0 to High(S) do begin VpUcs4ToIso88591(s[i], TempChar); TempStr[Succ(i)] := TempChar; end; raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sUnexpEndOfInput + QuotedStr(TempStr)); end; end; {--------} procedure TVpParser.ParseWhitespace; var TempChar : DOMChar; begin TempChar := ReadChar(False); while IsWhitespace(TempChar) do begin SkipChar; DataBufferAppend(TempChar); TempChar := ReadChar(False); end; end; {--------} procedure TVpParser.ParseXMLDeclaration; var sValue : DOMString; Buffer : DOMString; HasEntRef : Boolean; begin if FXMLDecParsed then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sXMLDecNotAtBeg); HasEntRef := False; SkipWhitespace(True); Require(Xpc_Version); DatabufferAppend('version'); ParseEq; DatabufferAppend('="'); Buffer := DatabufferToString; sValue := ReadLiteral(0, HasEntRef); ValidateVersNum(sValue); Buffer := Buffer + sValue + '"'; if (sValue <> VpXMLSpecification) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, Format(sInvalidXMLVersion, [VpXMLSpecification])); SkipWhitespace(True); if TryRead(Xpc_Encoding) then begin DatabufferAppend('encoding'); ParseEq; DataBufferAppend('="'); Buffer := Buffer + ' ' + DataBufferToString; sValue := ReadLiteral(LIT_CHAR_REF or LIT_ENTITY_REF, HasEntRef); ValidateEncName(sValue); Buffer := Buffer + sValue + '"'; if CompareText(sValue, 'ISO-8859-1') = 0 then FFilter.Format := sfISO88591; SkipWhitespace(True); end; if TryRead(Xpc_Standalone) then begin DatabufferAppend('standalone'); ParseEq; DatabufferAppend('="'); Buffer := Buffer + ' ' + DataBufferToString; sValue := ReadLiteral(LIT_CHAR_REF or LIT_ENTITY_REF, HasEntRef); if (not ((sValue = 'yes') or (sValue = 'no'))) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvStandAloneVal); Buffer := Buffer + sValue + '"'; FIsStandalone := sValue = 'yes'; SkipWhitespace(True) end; Require(Xpc_ProcessInstrEnd); DatabufferToString; DatabufferAppend(Buffer); end; {--------} procedure TVpParser.PopDocument; begin Assert(FDocStack.Count > 0); if FDocStack.Count > 0 then begin FFilter := FDocStack[Pred(FDocStack.Count)]; FDocStack.Delete(Pred(FDocStack.Count)); end; end; {--------} procedure TVpParser.PushDocument; begin Assert(Assigned(FFilter)); FDocStack.Add(Pointer(FFilter)); FFilter := nil; end; {--------} procedure TVpParser.PushString(const sVal : DOMString); var MemStream : TVpMemoryStream; TempString : string; begin if Length(sVal) > 0 then begin PushDocument; MemStream := TVpMemoryStream.Create; TempString := WideCharLenToString(Pointer(sVal), Length(sVal)); MemStream.Write(TempString[1], Length(TempString)); MemStream.Position := 0; FFilter := TVpInCharFilter.Create(MemStream, BufferSize); end; end; {--------} function TVpParser.ReadChar(const UpdatePos : Boolean) : DOMChar; begin Result := FFilter.ReadChar; if ((Result = VpEndOfStream) and (not IsEndDocument)) then Result := FFilter.ReadChar; if (UpdatePos) then FFilter.SkipChar; end; {--------} procedure TVpParser.ReadExternalIds(bInNotation : Boolean; var sIds : StringIds); var HasEntRef : Boolean; TempChar : DOMChar; begin HasEntRef := False; if TryRead(Xpc_ExternalPublic) then begin RequireWhitespace; sIds[0] := ReadLiteral(LIT_NORMALIZE, HasEntRef); ValidatePublicID(sIds[0]); if bInNotation then begin SkipWhitespace(True); TempChar := ReadChar(False); if (TempChar = '''') or (TempChar = '"') then sIds[1] := ReadLiteral(0, HasEntRef); end else begin RequireWhitespace; sIds[1] := ReadLiteral(0, HasEntRef); end; end else if TryRead(Xpc_ExternalSystem) then begin RequireWhitespace; sIds[1] := ReadLiteral(0, HasEntRef); end; end; {--------} function TVpParser.ReadLiteral(wFlags : Integer; var HasEntRef : Boolean) : DOMString; var TempStr : DOMString; cDelim, TempChar : DOMChar; EntRefs : TStringList; StackLevel : Integer; CurrCharRef : Boolean; begin StackLevel := 0; CurrCharRef := False; Result := ''; EntRefs := nil; cDelim := ReadChar(True); if (cDelim <> '"') and (cDelim <> #39) and (cDelim <> #126) and (cDelim <> #0) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sQuoteExpected); TempChar := ReadChar(False); while (not IsEndDocument) and ((CurrCharRef) or (TempChar <> cDelim)) do begin if (TempChar = #$0A) then begin TempChar := ' '; end else if (TempChar = #$0D) then TempChar := ' ' else if (TempChar = '&') then begin if wFlags and LIT_CHAR_REF <> 0 then begin if wFlags and LIT_ENTITY_REF <> 0 then CurrCharRef := True; HasEntRef := True; SkipChar; TempChar := ReadChar(False); if TempChar = '#' then begin SkipChar; ParseCharRef; TempChar := ReadChar(False); CurrCharRef := False; Continue; end else if wFlags and LIT_ENTITY_REF <> 0 then begin TempStr := ParseEntityRef(False); if (TempStr <> 'lt') and (TempStr <> 'gt') and (TempStr <> 'amp') and (TempStr <> 'apos') and (TempStr <> 'quot') then begin if (not Assigned(EntRefs)) then begin EntRefs := TStringList.Create; EntRefs.Sorted := True; EntRefs.Duplicates := dupError; StackLevel := FDocStack.Count; end else StackLevel := Succ(FDocStack.Count); try if FDocStack.Count = StackLevel then begin EntRefs.Clear; StackLevel := FDocStack.Count; end; EntRefs.Add(TempStr); except on E:EStringListError do begin EntRefs.Free; raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sCircularEntRef + TempChar); end; on E:EVpParserError do raise; end; end else HasEntRef := False; TempChar := ReadChar(False); Continue; end else if wFlags and LIT_PE_REF <> 0 then begin ParseParameterEntityRef(False, True); Continue; end else DataBufferAppend('&'); if (not Assigned(EntRefs)) then begin StackLevel := FDocStack.Count; EntRefs := TStringList.Create; EntRefs.Sorted := True; EntRefs.Duplicates := dupError; end; try if StackLevel = FDocStack.Count then begin EntRefs.Clear; StackLevel := FDocStack.Count; end; EntRefs.Add('&' + DOMString(TempChar)); except on E:EStringListError do begin EntRefs.Free; raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sCircularEntRef + TempChar); end; on E:EVpParserError do raise; end; end; end; DataBufferAppend(TempChar); SkipChar; TempChar := ReadChar(False); CurrCharRef := False; end; if TempChar <> cDelim then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, 'Expected: ' + cDelim); SkipChar; if wFlags and LIT_NORMALIZE <> 0 then DataBufferNormalize; Result := DataBufferToString; EntRefs.Free; end; {--------} function TVpParser.ReadNameToken(aValFirst : Boolean) : DOMString; var TempChar : DOMChar; First : Boolean; ResultLen : Integer; CurrLen : Integer; begin if TryRead(Xpc_ParamEntity) then begin ParseParameterEntityRef(True, False); SkipWhiteSpace(True); end; First := aValFirst; Result := ''; CurrLen := 0; ResultLen := 20; SetLength(Result, ResultLen); while True do begin TempChar := ReadChar(False); if (TempChar = '%') or (TempChar = '<') or (TempChar = '>') or (TempChar = '&') or (TempChar = ',') or (TempChar = '|') or (TempChar = '*') or (TempChar = '+') or (TempChar = '?') or (TempChar = ')') or (TempChar = '=') or (TempChar = #39) or (TempChar = '"') or (TempChar = '[') or (TempChar = ' ') or (TempChar = #9) or (TempChar = #$0A) or (TempChar = #$0D) or (TempChar = ';') or (TempChar = '/') or (TempChar = '') or (TempChar = #1) then Break else if ValidateNameChar(First, TempChar) then begin if (CurrLen + 2 > ResultLen) then begin ResultLen := ResultLen * 2; SetLength(Result, ResultLen); end; SkipChar; Move(TempChar, PByteArray(Pointer(Result))^[CurrLen], 2); Inc(CurrLen, 2); end else raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvalidName + QuotedStr(TempChar)); First := False; end; SetLength(Result, CurrLen div 2); end; {--------} procedure TVpParser.Require(const S : array of Longint); var TempStr : AnsiString; TempChar : AnsiChar; i : Integer; begin if not TryRead(S) then begin SetLength(TempStr, High(S) + 1); for i := 0 to High(S) do begin VpUcs4ToIso88591(s[i], TempChar); TempStr[i + 1] := TempChar; end; if ReadChar(False) = '&' then begin SkipChar; if ReadChar(False) = '#' then begin SkipChar; if ParseCharRef = TempStr then Exit; end; end; raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sExpectedString + QuotedStr(TempStr)); end; end; {--------} procedure TVpParser.RequireWhitespace; begin if IsWhitespace(ReadChar(False)) then SkipWhitespace(True) else raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sSpaceExpectedAt + 'Line: ' + IntToStr(FFilter.Line) + ' Position: ' + IntToStr(FFilter.LinePos)); end; {--------} procedure TVpParser.SetAttribute(const sElemName, sName : DOMString; wType : Integer; const sEnum, sValue : DOMString; wValueType : Integer); var wIdx : Integer; oElemInfo : TVpElementInfo; oAttrInfo : TVpAttributeInfo; begin wIdx := GetElementIndexOf(sElemName); if wIdx < 0 then begin SetElement(sElemName, CONTENT_UNDECLARED, ''); wIdx := GetElementIndexOf(sElemName); end; oElemInfo := TVpElementInfo(FElementInfo.Objects[wIdx]); oAttrInfo := TVpAttributeInfo.Create; oAttrInfo.AttrType := wType; oAttrInfo.Value := sValue; oAttrInfo.ValueType := wValueType; oAttrInfo.Enum := sEnum; oElemInfo.SetAttribute(sName, oAttrInfo); end; {--------} procedure TVpParser.SetElement(const sName : DOMString; wType : Integer; const sContentModel : DOMString); var oElem : TVpElementInfo; wIdx : Integer; begin wIdx := GetElementIndexOf(sName); if wIdx < 0 then begin oElem := TVpElementInfo.Create; FElementInfo.AddObject(sName, oElem); end else oElem := TVpElementInfo(FElementInfo.Objects[wIdx]); if wType <> CONTENT_UNDECLARED then oElem.ContentType := wType; if sContentModel <> '' then oElem.ContentModel := sContentModel; end; {--------} procedure TVpParser.SetEntity(const sEntityName : DOMString; wClass : Integer; const sPublicId, sSystemId, sValue, sNotationName : DOMString; aIsPE : Boolean); var wIdx : Integer; oEntity : TVpEntityInfo; begin wIdx := GetEntityIndexOf(sEntityName, aIsPE); if wIdx < 0 then begin oEntity := TVpEntityInfo.Create; oEntity.EntityClass := wClass; oEntity.PublicId := sPublicId; oEntity.SystemId := sSystemId; oEntity.Value := sValue; oEntity.NotationName := sNotationName; oEntity.IsParameterEntity := aIsPE; FEntityInfo.AddObject(sEntityName, oEntity); end; end; {--------} procedure TVpParser.SetInternalEntity(const sName, sValue : DOMString; aIsPE : Boolean); begin SetEntity(sName, ENTITY_INTERNAL, '', '', sValue, '', aIsPE); end; {--------} procedure TVpParser.SetNotation(const sNotationName, sPublicId, sSystemId : DOMString); var oNot : TVpNotationInfo; wIdx : Integer; begin if not FNotationInfo.Find(sNotationName, wIdx) then begin oNot := TVpNotationInfo.Create; oNot.PublicId := sPublicId; oNot.SystemId := sSystemId; FNotationInfo.AddObject(sNotationName, oNot); end; end; {--------} procedure TVpParser.SkipChar; begin FFilter.SkipChar; end; {--------} procedure TVpParser.SkipWhitespace(aNextDoc : Boolean); begin while (not FFilter.Eof) and (IsWhitespace(ReadChar(False))) do SkipChar; if aNextDoc then begin IsEndDocument; while (not FFilter.Eof) and (IsWhitespace(ReadChar(False))) do SkipChar; end; end; {--------} function TVpParser.TryRead(const S : array of Longint) : Boolean; begin Result := False; if (not IsEndDocument) then begin Result := FFilter.TryRead(S); IsEndDocument; end; end; {--------} procedure TVpParser.ValidateAttribute(const aValue : DOMString; HasEntRef : Boolean); begin if (not HasEntRef) then if (VpPos('<', aValue) <> 0) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvAttrChar + '''<''') else if (VpPos('&', aValue) <> 0) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvAttrChar + '''&''') else if (VpPos('"', aValue) <> 0) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvAttrChar + '''"'''); end; {--------} procedure TVpParser.ValidateCData(const CDATA : DOMString); begin if (VpPos(']]>', CDATA) <> 0) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvalidCDataSection); end; {--------} procedure TVpParser.ValidateElementName(const aName : DOMString); begin if (aName = '') or (aName = ' ') then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvalidElementName + QuotedStr(aName)); end; {--------} procedure TVpParser.ValidateEncName(const aValue : string); var i : Integer; Good : Boolean; begin { Production [81]} for i := 1 to Length(aValue) do begin Good := False; if ((aValue[i] >= 'A') and (aValue[i] <= 'z')) then Good := True else if i > 1 then if (aValue[i] >= '0') and (aValue[i] <= '9') then Good := True else if aValue[i] = '.' then Good := True else if aValue[i] = '_' then Good := True else if aValue[i] = '-' then Good := True else if aValue[i] = '=' then Good := True; if not Good then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvEncName + QuotedStr(aValue)); end; end; {--------} procedure TVpParser.ValidateEntityValue(const aValue : DOMString; aQuoteCh : DOMChar); var TempChr : DOMChar; i : Integer; begin for i := 1 to Length(aValue) do begin TempChr := aValue[i]; if (TempChr = '%') or (TempChr = '&') or (TempChr = aQuoteCh) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvEntityValue + QuotedStr(TempChr)); end; end; {--------} function TVpParser.ValidateNameChar(const First : Boolean; const Char : DOMChar) : Boolean; var BothUsed : Boolean; UCS4 : TVpUCS4Char; begin { Naming rules - from sect 2.3 of spec} { Names cannot be an empty string } { Names must begin with 1 letter or one of the following punctuation characters ['_',':']} { Names should not begin with 'XML' or any case derivitive} { Except for the first character, names can contain [letters, digits,'.', '-', '_', ':'} VpUtf16ToUcs4(DOMChar(PByteArray(@Char)^[0]), DOMChar(PByteArray(@Char)^[1]), UCS4, BothUsed); if not First then Result := VpIsNameChar(UCS4) else Result := VpIsNameCharFirst(UCS4); end; {--------} procedure TVpParser.ValidatePCData(const aString : DOMString; aInEntityRef : Boolean); begin if (not aInEntityRef) then if (VpRPos('<', aString) <> 0) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvPCData + '''<''') else if (VpRPos('&', aString) <> 0) and (VpRPos(';', aString) = 0) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvPCData + '''&''') else if (VpRPos(']]>', aString) <> 0) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvPCData + ''']]>'''); end; {--------} procedure TVpParser.ValidatePublicID(const aString : DOMString); var Ucs4Char : TVpUcs4Char; i : Integer; begin for i := 1 to Length(aString) do begin VpIso88591ToUcs4(AnsiChar(aString[i]), Ucs4Char); if (not VpIsPubidChar(Ucs4Char)) then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvPubIDChar + QuotedStr(aString[i])); end; end; {--------} procedure TVpParser.ValidateVersNum(const aString : string); var i : Integer; TempChr : char; Good : Boolean; begin for i := 1 to Length(aString) do begin Good := False; TempChr := aString[i]; if (TempChr >= 'A') and (TempChr <= 'z') then Good := True else if (TempChr >= '0') and (TempChr <= '9') then Good := True else if (TempChr = '.') then Good := True else if (TempChr = '_') then Good := True else if (TempChr = ':') then Good := True else if (TempChr = '-') then Good := True; if not Good then raise EVpParserError.CreateError (FFilter.Line, FFilter.LinePos, sInvVerNum + QuotedStr(aString)); end; end; end.