{ This file is part of the Web Service Toolkit Copyright (c) 2007-2014 by Inoussa OUEDRAOGO This file is provide under modified LGPL licence ( the files COPYING.modifiedLGPL and COPYING.LGPL). This program 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. } {$INCLUDE wst_global.inc} unit xsd_parser; interface uses Classes, SysUtils, {$IFNDEF FPC}xmldom, wst_delphi_xml{$ELSE}DOM{$ENDIF}, cursor_intf, rtti_filters, pastree, pascal_parser_intf, logger_intf, locators; type EXsdParserException = class(Exception) end; EXsdParserAssertException = class(EXsdParserException) end; EXsdTypeNotFoundException = class(EXsdParserException) end; EXsdInvalidDefinitionException = class(EXsdParserException) end; EXsdInvalidTypeDefinitionException = class(EXsdInvalidDefinitionException) end; EXsdInvalidElementDefinitionException = class(EXsdInvalidDefinitionException) end; TOnParserMessage = procedure (const AMsgType : TMessageType; const AMsg : string) of object; IDocumentLocator = locators.IDocumentLocator; TParserOption = ( poEnumAlwaysPrefix, // Always prefix enum item with the enum name poParsingIncludeSchema ); TParserOptions = set of TParserOption; IParserContext = interface ['{F400BA9E-41AC-456C-ABF9-CEAA75313685}'] function GetXsShortNames() : TStrings; function GetSymbolTable() : TwstPasTreeContainer; function FindNameSpace(const AShortName : string; out AResult : string) : Boolean; function FindShortNamesForNameSpace(const ANameSpace : string) : TStrings; function GetTargetNameSpace() : string; function GetTargetModule() : TPasModule; function GetDocumentLocator() : IDocumentLocator; procedure SetDocumentLocator(ALocator : IDocumentLocator); function GetSimpleOptions() : TParserOptions; procedure SetSimpleOptions(const AValue : TParserOptions); procedure AddTypeToCheck(AType : TPasType); procedure AddIncludedDoc(ADocLocation : string); function IsIncludedDoc(ADocLocation : string) : Boolean; end; IXsdPaser = interface ['{F0CEC726-A068-4CCC-B1E7-D31F018415B2}'] function FindParser(const ANamespace : string) : IXsdPaser; function ParseType( const AName, ATypeKind : string { ATypeKind "ComplexType", "SimpleType", "Element" } ) : TPasType; overload; function ParseType( const AName : string; const ATypeNode : TDOMNode ) : TPasType; overload; procedure ParseTypes(); procedure SetNotifier(ANotifier : TOnParserMessage); end; { TCustomXsdSchemaParser } TCustomXsdSchemaParser = class(TInterfacedObject, IInterface, IParserContext, IXsdPaser) private FDoc : TXMLDocument; FParentContext : Pointer;//IParserContext; FSymbols : TwstPasTreeContainer; FModuleName : string; FModule : TPasModule; FTargetNameSpace : string; FSchemaNode : TDOMNode; private FNameSpaceList : TStringList; FXSShortNames : TStrings; FChildCursor : IObjectCursor; FOnMessage: TOnParserMessage; FDocumentLocator : IDocumentLocator; FSimpleOptions : TParserOptions; FCheckedTypes : TList2; FImportParsed : Boolean; FXsdParsers : TStringList; FIncludeList : TStringList; FIncludeParsed : Boolean; FPrepared : Boolean; FOldNameKinds : TElementNameKinds; private procedure DoOnMessage(const AMsgType : TMessageType; const AMsg : string); private function FindNamedNode(AList : IObjectCursor; const AName : WideString; const AOrder : Integer = 0):TDOMNode; function GetParentContext() : IParserContext;{$IFDEF USE_INLINE}inline;{$ENDIF} function HasParentContext() : Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF} procedure Prepare(const AMustSucceed : Boolean); function FindElement(const AName: String) : TPasElement; overload;{$IFDEF USE_INLINE}inline;{$ENDIF} function FindElement(const AName: String; const ANameKinds : TElementNameKinds) : TPasElement; overload;{$IFDEF USE_INLINE}inline;{$ENDIF} protected function GetXsShortNames() : TStrings; function GetSymbolTable() : TwstPasTreeContainer; function FindNameSpace(const AShortName : string; out AResult : string) : Boolean; function FindShortNamesForNameSpaceLocal(const ANameSpace : string) : TStrings; function FindShortNamesForNameSpace(const ANameSpace : string) : TStrings; function GetDocumentLocator() : IDocumentLocator; procedure SetDocumentLocator(ALocator : IDocumentLocator); function GetSimpleOptions() : TParserOptions; procedure SetSimpleOptions(const AValue : TParserOptions); procedure AddTypeToCheck(AType : TPasType); procedure AddIncludedDoc(ADocLocation : string); function IsIncludedDoc(ADocLocation : string) : Boolean; procedure SetNotifier(ANotifier : TOnParserMessage); function InternalParseType( const AName : string; const ATypeNode : TDOMNode ) : TPasType; procedure CreateImportParsers(); procedure ParseImportDocuments(); virtual; procedure CreateIncludeList(); procedure ParseIncludeDocuments(); virtual; public constructor Create( ADoc : TXMLDocument; ASchemaNode : TDOMNode; ASymbols : TwstPasTreeContainer; AParentContext : IParserContext ); virtual; destructor Destroy();override; function FindParser(const ANamespace : string) : IXsdPaser; function ParseType( const AName, ATypeKind : string { ATypeKind "ComplexType", "SimpleType", "Element" } ) : TPasType; overload; function ParseType( const AName : string; const ATypeNode : TDOMNode ) : TPasType; overload; procedure ParseTypes(); function GetTargetNameSpace() : string; function GetTargetModule() : TPasModule; property SymbolTable : TwstPasTreeContainer read FSymbols; property Module : TPasModule read FModule; property OnMessage : TOnParserMessage read FOnMessage write FOnMessage; end; TCustomXsdSchemaParserClass = class of TCustomXsdSchemaParser; TXsdParser = class(TCustomXsdSchemaParser) public constructor Create( ADoc : TXMLDocument; ASymbols : TwstPasTreeContainer; const AModuleName : string; const ANotifier : TOnParserMessage = nil ); end; procedure CheckDuplicatedProperties( AClassList : TList2; ASymbolTable : TwstPasTreeContainer ); implementation uses ws_parser_imp, dom_cursors, parserutils, xsd_consts, wst_consts {$IFDEF FPC} ,wst_fpc_xml {$ENDIF} ; function NodeValue(const ANode : TDOMNode) : DOMString;{$IFDEF USE_INLINE}inline;{$ENDIF} begin if ( ANode = nil ) then Result := '' else Result := ANode.NodeValue; end; function HasProp(AClass : TPasClassType; const AProp : string) : Boolean; var i : Integer; ml : TList2; e : TPasElement; begin Result := False; ml := AClass.Members; for i := 0 to ml.Count - 1 do begin e := TPasElement(ml[i]); if e.InheritsFrom(TPasProperty) and SameText(e.Name,AProp) then begin Result := True; Break; end; end; end; procedure CheckDuplicatedProperties( AClassList : TList2; ASymbolTable : TwstPasTreeContainer ); var i, k : Integer; locItem : TPasClassType; locAncestor : TPasType; e : TPasElement; begin for i := 0 to AClassList.Count-1 do begin locItem := TPasClassType(AClassList[i]); if (locItem.Members.Count = 0) then Continue; locAncestor := locItem.AncestorType; while (locAncestor <> nil) do begin if locAncestor.InheritsFrom(TPasUnresolvedTypeRef) then begin e := ASymbolTable.FindElement(ASymbolTable.GetExternalName(locAncestor)); if (e = nil) or not(e.InheritsFrom(TPasType)) then Break; locAncestor := e as TPasType; end; if not locAncestor.InheritsFrom(TPasClassType) then Break; if (TPasClassType(locAncestor).Members.Count = 0) then Break; k := 0; while (k < locItem.Members.Count) do begin e := TPasElement(locItem.Members[k]); if not e.InheritsFrom(TPasProperty) then Continue; if HasProp(TPasClassType(locAncestor),e.Name) then begin locItem.Members.Delete(k); e.Release(); Continue; end; k := k + 1; end; locAncestor := TPasClassType(locAncestor).AncestorType; end; end; end; { TCustomXsdSchemaParser } constructor TCustomXsdSchemaParser.Create( ADoc : TXMLDocument; ASchemaNode : TDOMNode; ASymbols : TwstPasTreeContainer; AParentContext : IParserContext ); begin if ( ADoc = nil ) then raise EXsdParserAssertException.Create(SERR_InvalidDomDocument); if ( ASchemaNode = nil ) then raise EXsdParserAssertException.Create(SERR_InvalidSchemaNode); if ( ASymbols = nil ) then raise EXsdParserAssertException.Create(SERR_InvalidSymbolTable); FDoc := ADoc; FParentContext := Pointer(AParentContext); FSymbols := ASymbols; FOldNameKinds := FSymbols.DefaultSearchNameKinds; FSymbols.DefaultSearchNameKinds := [elkDeclaredName]; FSchemaNode := ASchemaNode; FNameSpaceList := TStringList.Create(); FNameSpaceList.Duplicates := dupError; FNameSpaceList.Sorted := True; Prepare(False); end; destructor TCustomXsdSchemaParser.Destroy(); procedure FreeList(AList : TStrings); var j : PtrInt; begin if Assigned(AList) and (AList.Count > 0) then begin for j := Pred(AList.Count) downto 0 do begin AList.Objects[j].Free(); AList.Objects[j] := nil; end; AList.Free(); end; end; begin if (FSymbols <> nil) then FSymbols.DefaultSearchNameKinds := FOldNameKinds; FParentContext := nil; FreeAndNil(FIncludeList); FreeList(FNameSpaceList); FreeList(FXsdParsers); FCheckedTypes.Free(); inherited; end; function TCustomXsdSchemaParser.FindParser(const ANamespace : string) : IXsdPaser; var i : PtrInt; p, p1 : IXsdPaser; begin Prepare(True); Result := nil; if (ANamespace = FTargetNameSpace) then begin Result := Self; Exit; end; if (FXsdParsers = nil) then CreateImportParsers(); if (FXsdParsers = nil) then Exit; i := FXsdParsers.IndexOf(ANamespace); if ( i >= 0 ) then begin Result := (FXsdParsers.Objects[i] as TIntfObjectRef).Intf as IXsdPaser; end else begin for i := 0 to Pred(FXsdParsers.Count) do begin p := (FXsdParsers.Objects[i] as TIntfObjectRef).Intf as IXsdPaser; p1 := p.FindParser(ANamespace); if (p1 <> nil) then begin Result := p1; Break; end; end; end; end; procedure TCustomXsdSchemaParser.DoOnMessage( const AMsgType: TMessageType; const AMsg: string ); begin if Assigned(FOnMessage) then begin FOnMessage(AMsgType,AMsg); end else if IsConsole and HasLogger() then begin GetLogger().Log(AMsgType, AMsg); end; end; function TCustomXsdSchemaParser.FindElement(const AName: String): TPasElement; begin Result := SymbolTable.FindElementInModule(AName,FModule); if ( Result = nil ) then Result := SymbolTable.FindElement(AName); end; function TCustomXsdSchemaParser.FindElement( const AName : String; const ANameKinds : TElementNameKinds ) : TPasElement; begin Result := SymbolTable.FindElementInModule(AName,FModule,ANameKinds); if ( Result = nil ) then Result := SymbolTable.FindElement(AName,ANameKinds); end; procedure TCustomXsdSchemaParser.ParseImportDocuments(); var locOldCurrentModule : TPasModule; i : Integer; p : IXsdPaser; begin if FImportParsed then Exit; CreateImportParsers(); if (FXsdParsers = nil) then Exit; FImportParsed := True; if Assigned(FChildCursor) then begin locOldCurrentModule := SymbolTable.CurrentModule; try for i := 0 to FXsdParsers.Count - 1 do begin p := TIntfObjectRef(FXsdParsers.Objects[i]).Intf as IXsdPaser; p.ParseTypes(); end; finally SymbolTable.SetCurrentModule(locOldCurrentModule); end; end; end; procedure TCustomXsdSchemaParser.CreateIncludeList(); begin if (FIncludeList = nil) then begin FIncludeList := TStringList.Create(); FIncludeList.Duplicates := dupIgnore; FIncludeList.Sorted := True; end; end; procedure TCustomXsdSchemaParser.ParseIncludeDocuments(); var crsSchemaChild : IObjectCursor; strFilter, locFileName : string; includeNode : TDOMElement; includeDoc : TXMLDocument; locParser : IXsdPaser; locOldCurrentModule : TPasModule; locLocator, locTempLocator : IDocumentLocator; locContext : IParserContext; locUsesList : TList2; locModule : TPasModule; locName, s : string; i : Integer; begin if FIncludeParsed then exit; Prepare(True); if (poParsingIncludeSchema in FSimpleOptions) then begin locContext := GetParentContext(); if (locContext = nil) then raise EXsdParserAssertException.CreateFmt(SERR_InvalidParserState,['"poParsingIncludeSchema" require a parent context']); if not(IsStrEmpty(FTargetNameSpace)) and (FTargetNameSpace <> locContext.GetTargetNameSpace()) then raise EXsdParserAssertException.Create(SERR_InvalidIncludeDirectiveNS); end; FIncludeParsed := True; locLocator := GetDocumentLocator(); if (locLocator = nil) then Exit; if Assigned(FChildCursor) then begin locOldCurrentModule := SymbolTable.CurrentModule; try locUsesList := FModule.InterfaceSection.UsesList; crsSchemaChild := FChildCursor.Clone() as IObjectCursor; strFilter := CreateQualifiedNameFilterStr(s_include,FXSShortNames); crsSchemaChild := CreateCursorOn(crsSchemaChild,ParseFilter(strFilter,TDOMNodeRttiExposer)); crsSchemaChild.Reset(); while crsSchemaChild.MoveNext() do begin includeNode := (crsSchemaChild.GetCurrent() as TDOMNodeRttiExposer).InnerObject as TDOMElement; if (includeNode.Attributes <> nil) and (includeNode.Attributes.Length > 0) then begin locFileName := NodeValue(includeNode.Attributes.GetNamedItem(s_schemaLocation)); if not(IsStrEmpty(locFileName) or IsIncludedDoc(locFileName)) then begin if locLocator.Find(locFileName,includeDoc) then begin AddIncludedDoc(locFileName); locParser := TCustomXsdSchemaParserClass(Self.ClassType).Create( includeDoc, includeDoc.DocumentElement, SymbolTable, Self as IParserContext ); locContext := locParser as IParserContext; locContext.SetSimpleOptions(locContext.GetSimpleOptions() + [poParsingIncludeSchema]); locTempLocator := locLocator.Clone(); locTempLocator.SetBasePath(locLocator.FindPath(locFileName)); locContext.SetDocumentLocator(locTempLocator); locParser.SetNotifier(FOnMessage); locParser.ParseTypes(); locModule := locContext.GetTargetModule(); if (ExtractIdentifier(locContext.GetTargetNameSpace()) = locModule.Name) then begin s := ChangeFileExt(ExtractFileName(locFileName),''); i := 1; locName := s; while (FSymbols.FindModule(locName) <> nil) do begin locName := Format('%s%d',[s,i]); Inc(i); end; locModule.Name := locName; end; if (locModule <> FModule) and (locUsesList.IndexOf(locModule) = -1) then begin locModule.AddRef(); locUsesList.Add(locModule); end; end else begin DoOnMessage(mtError,Format(SERR_FileNotFound,[locFileName])); end; end; end; end; finally SymbolTable.SetCurrentModule(locOldCurrentModule); end; end; end; function TCustomXsdSchemaParser.FindNamedNode( AList : IObjectCursor; const AName : WideString; const AOrder : Integer ): TDOMNode; var attCrs, crs : IObjectCursor; curObj : TDOMNodeRttiExposer; fltr : IObjectFilter; locOrder : Integer; begin Result := nil; if Assigned(AList) then begin fltr := ParseFilter(Format('%s = %s',[s_NODE_NAME,QuotedStr(s_name)]),TDOMNodeRttiExposer); AList.Reset(); locOrder := AOrder; while AList.MoveNext() do begin curObj := AList.GetCurrent() as TDOMNodeRttiExposer; attCrs := CreateAttributesCursor(curObj.InnerObject,cetRttiNode); if Assigned(attCrs) then begin crs := CreateCursorOn(attCrs,fltr); crs.Reset(); if crs.MoveNext() and AnsiSameText(AName,TDOMNodeRttiExposer(crs.GetCurrent()).NodeValue) then begin Dec(locOrder); if ( locOrder <= 0 ) then begin Result := curObj.InnerObject; exit; end; end; end; end; end; end; function TCustomXsdSchemaParser.FindNameSpace( const AShortName : string; out AResult : string ) : Boolean; var i : PtrInt; ls : TStrings; pc : IParserContext; begin AResult := ''; Result := False; for i := 0 to Pred(FNameSpaceList.Count) do begin ls := FNameSpaceList.Objects[i] as TStrings; if ( ls.IndexOf(AShortName) >= 0 ) then begin AResult := FNameSpaceList[i]; Result := True; Break; end; end; if not Result then begin pc := GetParentContext(); if ( pc <> nil ) then Result := GetParentContext().FindNameSpace(AShortName,AResult); end; end; function TCustomXsdSchemaParser.FindShortNamesForNameSpace(const ANameSpace: string): TStrings; var prtCtx : IParserContext; begin Result := FindShortNamesForNameSpaceLocal(ANameSpace); if ( Result = nil ) then begin prtCtx := GetParentContext(); if Assigned(prtCtx) then Result := prtCtx.FindShortNamesForNameSpace(ANameSpace); end; end; function TCustomXsdSchemaParser.GetDocumentLocator(): IDocumentLocator; begin Result := FDocumentLocator; if (Result = nil) and (FParentContext <> nil) then Result := GetParentContext().GetDocumentLocator(); end; procedure TCustomXsdSchemaParser.SetDocumentLocator(ALocator: IDocumentLocator); begin FDocumentLocator := ALocator; end; function TCustomXsdSchemaParser.GetSimpleOptions(): TParserOptions; begin Result := FSimpleOptions; end; procedure TCustomXsdSchemaParser.SetSimpleOptions(const AValue: TParserOptions); begin if ( AValue <> FSimpleOptions ) then FSimpleOptions := AValue; end; procedure TCustomXsdSchemaParser.AddTypeToCheck(AType: TPasType); begin if (AType = nil) then exit; if HasParentContext() then begin GetParentContext().AddTypeToCheck(AType); exit; end; if (FCheckedTypes = nil) then FCheckedTypes := TList2.Create(); if (FCheckedTypes.IndexOf(AType) = -1) then FCheckedTypes.Add(AType); end; procedure TCustomXsdSchemaParser.AddIncludedDoc(ADocLocation : string); begin if (poParsingIncludeSchema in FSimpleOptions) then begin GetParentContext().AddIncludedDoc(ADocLocation); exit; end; if (FIncludeList = nil) then CreateIncludeList(); FIncludeList.Add(ADocLocation); end; function TCustomXsdSchemaParser.IsIncludedDoc(ADocLocation : string) : Boolean; begin Result := False; if (poParsingIncludeSchema in FSimpleOptions) then Result := GetParentContext().IsIncludedDoc(ADocLocation); if not Result then Result := (FIncludeList <> nil) and (FIncludeList.IndexOf(ADocLocation) <> -1); end; procedure TCustomXsdSchemaParser.SetNotifier(ANotifier: TOnParserMessage); begin FOnMessage := ANotifier; end; function TCustomXsdSchemaParser.FindShortNamesForNameSpaceLocal(const ANameSpace: string): TStrings; var i : PtrInt; begin i := FNameSpaceList.IndexOf(ANameSpace); if ( i >= 0 ) then Result := FNameSpaceList.Objects[i] as TStrings else Result := nil; end; function TCustomXsdSchemaParser.GetParentContext() : IParserContext; begin Result := IParserContext(FParentContext); end; function TCustomXsdSchemaParser.HasParentContext() : Boolean; begin Result := (FParentContext <> nil); end; function TCustomXsdSchemaParser.GetSymbolTable() : TwstPasTreeContainer; begin Result := FSymbols; end; function TCustomXsdSchemaParser.GetTargetModule() : TPasModule; begin Prepare(True); Result := FModule; end; function TCustomXsdSchemaParser.GetTargetNameSpace() : string; begin Prepare(True); Result := FTargetNameSpace; end; function TCustomXsdSchemaParser.GetXsShortNames() : TStrings; begin Result := FXSShortNames; end; function TCustomXsdSchemaParser.ParseType(const AName, ATypeKind : string): TPasType; begin Result := InternalParseType(AName,nil); end; function TCustomXsdSchemaParser.ParseType( const AName : string; const ATypeNode : TDOMNode ) : TPasType; begin Result := InternalParseType(AName,ATypeNode); end; function TCustomXsdSchemaParser.InternalParseType( const AName : string; const ATypeNode : TDOMNode ): TPasType; var crsSchemaChild : IObjectCursor; typNd : TDOMNode; typName : string; embededType : Boolean; localTypeName : string; procedure Init(); begin crsSchemaChild := FChildCursor.Clone() as IObjectCursor; end; function ExtractTypeHint(AElement: TDOMNode): string; begin if not wst_findCustomAttributeXsd(FXSShortNames,AElement,s_WST_typeHint,Result) then Result := ''; end; function FindTypeNode(out ASimpleTypeAlias : TPasType) : Boolean; var nd, oldTypeNode : TDOMNode; crs : IObjectCursor; locStrFilter, locTypeHint : string; locHintedType : TPasType; begin ASimpleTypeAlias := nil; Result := True; if ( ATypeNode <> nil ) then typNd := ATypeNode else typNd := FindNamedNode(crsSchemaChild,localTypeName); if not Assigned(typNd) then raise EXsdTypeNotFoundException.CreateFmt(SERR_TypeDefinitionNotFound,['1',AName]); if AnsiSameText(ExtractNameFromQName(typNd.NodeName),s_element) or AnsiSameText(ExtractNameFromQName(typNd.NodeName),s_attribute) then begin crs := CreateCursorOn(CreateAttributesCursor(typNd,cetRttiNode),ParseFilter(Format('%s = %s',[s_NODE_NAME,QuotedStr(s_type)]),TDOMNodeRttiExposer)); crs.Reset(); if crs.MoveNext() then begin nd := (crs.GetCurrent() as TDOMNodeRttiExposer).InnerObject; ASimpleTypeAlias := FindElement(ExtractNameFromQName(nd.NodeValue)) as TPasType; if Assigned(ASimpleTypeAlias) then begin if ASimpleTypeAlias.InheritsFrom(TPasNativeSimpleType) then begin locTypeHint := ExtractTypeHint(typNd); if not IsStrEmpty(locTypeHint) then begin locHintedType := FindElement(locTypeHint,[elkName]) as TPasType; if ( locHintedType <> nil ) then ASimpleTypeAlias := locHintedType; end; end; Result := False; end else begin oldTypeNode := typNd; typNd := FindNamedNode(crsSchemaChild,ExtractNameFromQName(nd.NodeValue)); if not Assigned(typNd) then raise EXsdTypeNotFoundException.CreateFmt(SERR_TypeDefinitionNotFound,['2',AName]); embededType := False; if ( typNd = oldTypeNode ) then begin typNd := FindNamedNode(crsSchemaChild,ExtractNameFromQName(nd.NodeValue),2); if not Assigned(typNd) then raise EXsdTypeNotFoundException.CreateFmt(SERR_TypeDefinitionNotFound,['2.1',AName]); end; end; end else begin //locStrFilter := Format('%s = %s or %s = %s ',[s_NODE_NAME,QuotedStr(s_complexType),s_NODE_NAME,QuotedStr(s_simpleType)]); locStrFilter := CreateQualifiedNameFilterStr(s_complexType,FXSShortNames) + ' or ' + CreateQualifiedNameFilterStr(s_simpleType,FXSShortNames); crs := CreateCursorOn(CreateChildrenCursor(typNd,cetRttiNode),ParseFilter(locStrFilter,TDOMNodeRttiExposer)); crs.Reset(); if not crs.MoveNext() then begin raise EXsdTypeNotFoundException.CreateFmt(SERR_TypeDefinitionNotFound,['3',AName]); end; typNd := (crs.GetCurrent() as TDOMNodeRttiExposer).InnerObject; typName := ExtractNameFromQName(AName); embededType := True; end; end; end; function ParseComplexType():TPasType; var locParser : TComplexTypeParser; begin locParser := TComplexTypeParser.Create(Self,typNd,typName,embededType); try Result := locParser.Parse(); finally FreeAndNil(locParser); end; end; function ParseSimpleType():TPasType; var locParser : TSimpleTypeParser; begin locParser := TSimpleTypeParser.Create(Self,typNd,typName,embededType); try Result := locParser.Parse(); finally FreeAndNil(locParser); end; end; function CreateTypeAlias(const ABase : TPasType): TPasType; var hasInterName : Boolean; baseName,internalName : string; begin baseName := ExtractNameFromQName(AName); internalName := ExtractIdentifier(baseName); hasInterName := IsReservedKeyWord(internalName) or ( not IsValidIdent(internalName) ); if hasInterName then begin internalName := '_' + internalName; end; Result := TPasType(SymbolTable.CreateElement(TPasAliasType,internalName,SymbolTable.CurrentModule.InterfaceSection,visDefault,'',0)); SymbolTable.RegisterExternalAlias(Result,baseName); TPasAliasType(Result).DestType := ABase; ABase.AddRef(); end; function CreateUnresolveType(): TPasType; var hasInternameName : Boolean; internameName, baseName : string; begin baseName := ExtractNameFromQName(AName); internameName := ExtractIdentifier(baseName); hasInternameName := IsReservedKeyWord(baseName) or (not IsValidIdent(internameName)); if hasInternameName then begin internameName := '_' + internameName; end; Result := TPasUnresolvedTypeRef(SymbolTable.CreateElement(TPasUnresolvedTypeRef,internameName,SymbolTable.CurrentModule.InterfaceSection,visDefault,'',0)); if not AnsiSameText(internameName,baseName) then SymbolTable.RegisterExternalAlias(Result,baseName); end; var frwType, aliasType : TPasType; sct : TPasSection; shortNameSpace, longNameSpace : string; typeModule : TPasModule; locTypeNodeFound : Boolean; begin Prepare(True); if not FImportParsed then ParseImportDocuments(); if not FIncludeParsed then ParseIncludeDocuments(); sct := nil; DoOnMessage(mtInfo, Format(SERR_Parsing,[AName])); try embededType := False; aliasType := nil; ExplodeQName(AName,localTypeName,shortNameSpace); if IsStrEmpty(shortNameSpace) then begin typeModule := FModule; end else begin if not FindNameSpace(shortNameSpace,longNameSpace) then raise EXsdParserAssertException.CreateFmt(SERR_UnableToResolveNamespace,[shortNameSpace]); typeModule := SymbolTable.FindModule(longNameSpace); end; if ( typeModule = nil ) then raise EXsdTypeNotFoundException.Create(AName); Result := SymbolTable.FindElementInModule(localTypeName,typeModule) as TPasType; if (Result <> nil) and (not Result.InheritsFrom(TPasUnresolvedTypeRef)) then Exit; Init(); locTypeNodeFound := FindTypeNode(aliasType); if ( Result <> nil ) and ( typeModule = FModule ) and ( not Result.InheritsFrom(TPasUnresolvedTypeRef) ) then begin if locTypeNodeFound and ( embededType <> ( SymbolTable.Properties.GetValue(Result,sEMBEDDED_TYPE) = '1' ) ) then Result := nil; end; if ( ( Result = nil ) or Result.InheritsFrom(TPasUnresolvedTypeRef) ) and ( typeModule = FModule ) then begin sct := FModule.InterfaceSection; frwType := Result; Result := nil; Init(); if locTypeNodeFound {FindTypeNode(aliasType)} then begin if AnsiSameText(ExtractNameFromQName(typNd.NodeName),s_complexType) then begin Result := ParseComplexType(); end else if AnsiSameText(ExtractNameFromQName(typNd.NodeName),s_simpleType) then begin Result := ParseSimpleType(); end; if Assigned(Result) then begin if Assigned(frwType) and AnsiSameText(SymbolTable.GetExternalName(Result),SymbolTable.GetExternalName(frwType)) then begin Result.Name := frwType.Name; SymbolTable.RegisterExternalAlias(Result,SymbolTable.GetExternalName(frwType)); end; end else begin raise EXsdTypeNotFoundException.CreateFmt(SERR_TypeNodeFoundButUnableToParseIt,[AName]); end; end else begin Result := CreateTypeAlias(aliasType); end; if ( frwType <> nil ) then begin sct.Declarations.Extract(frwType); sct.Types.Extract(frwType); frwType.Release(); end; sct.Declarations.Add(Result); sct.Types.Add(Result); if Result.InheritsFrom(TPasClassType) then begin sct.Classes.Add(Result); end; end; except on e : EXsdTypeNotFoundException do begin Result := CreateUnresolveType(); if ( sct = nil ) then sct := FModule.InterfaceSection; sct.Declarations.Add(Result); sct.Types.Add(Result); end; end; end; procedure TCustomXsdSchemaParser.CreateImportParsers(); var crsSchemaChild : IObjectCursor; strFilter, locFileName, locNameSpace : string; importNode : TDOMElement; importDoc : TXMLDocument; locParser : IXsdPaser; locOldCurrentModule : TPasModule; locContinue : Boolean; locLocator, loctempLocator : IDocumentLocator; locContext : IParserContext; locUsesList : TList2; locModule : TPasModule; locName, s : string; i : Integer; begin if FImportParsed then Exit; Prepare(True); locLocator := GetDocumentLocator(); if (locLocator = nil) then Exit; if Assigned(FChildCursor) then begin locOldCurrentModule := SymbolTable.CurrentModule; try locUsesList := FModule.InterfaceSection.UsesList; crsSchemaChild := FChildCursor.Clone() as IObjectCursor; strFilter := CreateQualifiedNameFilterStr(s_import,FXSShortNames); crsSchemaChild := CreateCursorOn(crsSchemaChild,ParseFilter(strFilter,TDOMNodeRttiExposer)); crsSchemaChild.Reset(); while crsSchemaChild.MoveNext() do begin importNode := (crsSchemaChild.GetCurrent() as TDOMNodeRttiExposer).InnerObject as TDOMElement; if ( importNode.Attributes <> nil ) and ( importNode.Attributes.Length > 0 ) then begin locFileName := NodeValue(importNode.Attributes.GetNamedItem(s_schemaLocation)); if not IsStrEmpty(locFileName) then begin if locLocator.Find(locFileName,importDoc) then begin locNameSpace := NodeValue(importNode.Attributes.GetNamedItem(s_namespace)); locContinue := IsStrEmpty(locNameSpace) or (FXsdParsers = nil) or (FXsdParsers.IndexOf(locNameSpace) = -1);//( SymbolTable.FindModule(locNameSpace) = nil ); if locContinue then begin if (FXsdParsers = nil) then begin FXsdParsers := TStringList.Create(); FXsdParsers.Duplicates := dupIgnore; FXsdParsers.Sorted := True; end; locParser := TCustomXsdSchemaParserClass(Self.ClassType).Create( importDoc, importDoc.DocumentElement, SymbolTable, Self as IParserContext ); locContext := locParser as IParserContext; loctempLocator := locLocator.Clone(); loctempLocator.SetBasePath(locLocator.FindPath(locFileName)); locContext.SetDocumentLocator(loctempLocator); FXsdParsers.AddObject(locNameSpace,TIntfObjectRef.Create(locParser)); locParser.SetNotifier(FOnMessage); //locParser.ParseTypes(); locModule := locContext.GetTargetModule(); if (locModule <> FModule) and (locUsesList.IndexOf(locModule) = -1) then begin s := ChangeFileExt(ExtractFileName(locFileName),''); s := ExtractIdentifier(s); i := 1; locName := s; while (FSymbols.FindModule(locName) <> nil) do begin locName := Format('%s%d',[s,i]); Inc(i); end; locModule.Name := locName; locModule.AddRef(); locUsesList.Add(locModule); if (FSymbols.Properties.GetValue(locModule,sFILE_NAME) = '') then FSymbols.Properties.SetValue(locModule,sFILE_NAME,locFileName); end; end; end else begin DoOnMessage(mtError,Format(SERR_FileNotFound,[locFileName])); end; end; end; end; finally SymbolTable.SetCurrentModule(locOldCurrentModule); end; end; end; procedure TCustomXsdSchemaParser.ParseTypes(); var crsSchemaChild, typTmpCrs : IObjectCursor; typFilterStr : string; typNode : TDOMNode; begin Prepare(True); ParseImportDocuments(); ParseIncludeDocuments(); if Assigned(FChildCursor) then begin crsSchemaChild := FChildCursor.Clone() as IObjectCursor; typFilterStr := Format( '%s or %s or %s or %s', [ CreateQualifiedNameFilterStr(s_complexType,FXSShortNames), CreateQualifiedNameFilterStr(s_simpleType,FXSShortNames), CreateQualifiedNameFilterStr(s_element,FXSShortNames), CreateQualifiedNameFilterStr(s_attribute,FXSShortNames) ] ); crsSchemaChild := CreateCursorOn(crsSchemaChild,ParseFilter(typFilterStr,TDOMNodeRttiExposer)); crsSchemaChild.Reset(); while crsSchemaChild.MoveNext() do begin typNode := (crsSchemaChild.GetCurrent() as TDOMNodeRttiExposer).InnerObject; typTmpCrs := CreateAttributesCursor(typNode,cetRttiNode); if Assigned(typTmpCrs) then begin typTmpCrs.Reset(); typTmpCrs := CreateCursorOn(typTmpCrs,ParseFilter(Format('%s=%s',[s_NODE_NAME,QuotedStr(s_name)]),TDOMNodeRttiExposer)); typTmpCrs.Reset(); if typTmpCrs.MoveNext() then begin InternalParseType( (typTmpCrs.GetCurrent() as TDOMNodeRttiExposer).NodeValue, typNode ); end; end; end; end; if (FCheckedTypes <> nil) and (FCheckedTypes.Count > 0) then CheckDuplicatedProperties(FCheckedTypes,FSymbols); end; procedure TCustomXsdSchemaParser.Prepare(const AMustSucceed : Boolean); var locAttCursor : IObjectCursor; prntCtx : IParserContext; nd : TDOMNode; i : PtrInt; ls : TStrings; ok : Boolean; begin if FPrepared then exit; FTargetNameSpace := ''; ok := False; if (FSchemaNode.Attributes <> nil) and (GetNodeListCount(FSchemaNode.Attributes) > 0) then begin nd := FSchemaNode.Attributes.GetNamedItem(s_targetNamespace); if (nd <> nil) then begin FTargetNameSpace := nd.NodeValue; ok := True; end; end; prntCtx := GetParentContext(); if not ok then begin if (poParsingIncludeSchema in FSimpleOptions) and (prntCtx <> nil) then begin FTargetNameSpace := prntCtx.GetTargetNameSpace(); ok := True; end else begin if not AMustSucceed then exit; raise EXsdParserAssertException.CreateFmt(SERR_SchemaNodeRequiredAttribute,[s_targetNamespace]); end; end; FPrepared := True; if IsStrEmpty(FModuleName) then FModuleName := ExtractIdentifier(FTargetNameSpace); if ( SymbolTable.FindModule(s_xs) = nil ) then begin CreateWstInterfaceSymbolTable(SymbolTable); end; FChildCursor := CreateChildrenCursor(FSchemaNode,cetRttiNode); locAttCursor := CreateAttributesCursor(FSchemaNode,cetRttiNode); BuildNameSpaceList(locAttCursor,FNameSpaceList); FXSShortNames := FindShortNamesForNameSpaceLocal(s_xs); if ( FXSShortNames = nil ) then begin if ( prntCtx = nil ) then raise EXsdParserAssertException.CreateFmt(SERR_InvalidSchemaDoc_NamespaceNotFound,[s_xs]); FXSShortNames := prntCtx.FindShortNamesForNameSpace(s_xs); if ( FXSShortNames = nil ) then raise EXsdParserAssertException.CreateFmt(SERR_InvalidSchemaDoc_NamespaceNotFoundShort,[s_xs]); end; if Assigned(prntCtx) then begin for i:= 0 to Pred(FNameSpaceList.Count) do begin ls := prntCtx.FindShortNamesForNameSpace(FNameSpaceList[i]); if Assigned(ls) then (FNameSpaceList.Objects[i] as TStrings).AddStrings(ls); end; end; FModule := SymbolTable.FindModule(FTargetNameSpace); if ( FModule = nil ) then begin FModule := TPasModule(SymbolTable.CreateElement(TPasModule,FModuleName,SymbolTable.Package,visDefault,'',0)); SymbolTable.Package.Modules.Add(FModule); SymbolTable.RegisterExternalAlias(FModule,FTargetNameSpace); FModule.InterfaceSection := TInterfaceSection(SymbolTable.CreateElement(TInterfaceSection,'',FModule,visDefault,'',0)); end; end; { TXsdParser } constructor TXsdParser.Create( ADoc : TXMLDocument; ASymbols : TwstPasTreeContainer; const AModuleName : string; const ANotifier : TOnParserMessage ); var locName : string; begin inherited Create(ADoc,ADoc.DocumentElement,ASymbols,nil); if Assigned(ANotifier) then FOnMessage := ANotifier; if not IsStrEmpty(AModuleName) then begin locName := ExtractIdentifier(AModuleName); if not IsStrEmpty(locName) then begin FModuleName := locName; if (Module <> nil) then Module.Name := FModuleName; end; end; end; end.