[JEDI.NET-commits] tools/JediDoc/source JediDoc.System.Analysis.pas,NONE,1.1 JediDoc.System.Console.
Status: Pre-Alpha
Brought to you by:
jedi_mbe
From: Marcel B. <jed...@us...> - 2005-03-28 12:26:25
|
Update of /cvsroot/jedidotnet/tools/JediDoc/source In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28000/tools/JediDoc/source Added Files: JediDoc.System.Analysis.pas JediDoc.System.Console.pas JediDoc.System.InfoClasses.pas JediDoc.System.Project.pas JediDoc.System.Xml.AssemblyDocGeneration.pas JediDoc.System.Xml.Reflection.pas JediDoc.System.Xml.UnitDocGeneration.pas Log Message: JediDoc tool initial check-in --- NEW FILE: JediDoc.System.Xml.UnitDocGeneration.pas --- {--------------------------------------------------------------------------------------------------- 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/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JediDoc.System.Xml.UnitDocGeneration.pas, released on 2005-03-28. The Initial Developer of the Original Code is Marcel Bestebroer Portions created by Marcel Bestebroer are Copyright (C) 2004 Marcel Bestebroer All Rights Reserved. Contributor(s): You may retrieve the latest version of this file at the JEDI.NET home page, located at http://sf.net/projects/jedidotnet Known Issues: ---------------------------------------------------------------------------------------------------} // $Id: JediDoc.System.Xml.UnitDocGeneration.pas,v 1.1 2005/03/28 12:26:16 jedi_mbe Exp $ unit JediDoc.System.Xml.UnitDocGeneration; interface {$REGION 'uses'} uses Jedi.IO.Paths, JediDoc.System.InfoClasses, JediDoc.System.Xml.Reflection, System.Collections, System.IO, System.Xml; {$ENDREGION} {$REGION 'Unit-based documentation generation'} type UnitDocs = class {$REGION 'Constructors'} strict private class constructor Create; strict protected constructor Create(outPath, fileName: string); {$ENDREGION} {$REGION 'Data'} strict private FUnit: string; FWriter: XmlWriter; strict private class var FUnits: Hashtable; {$ENDREGION} {$REGION 'Protected methods'} strict protected procedure CheckException(member: MemberInfo; memberNode: XmlNode); procedure CheckNode(memberNode: XmlNode; element: string; required: Boolean); overload; procedure CheckNode(memberNode: XmlNode; element, attribute, attributeValue: string; required: Boolean); overload; procedure CheckNodes(memberNode: XmlNode; element: string); procedure CheckParams(member: MemberInfo; memberNode: XmlNode); procedure CheckOverload(member: MemberInfo; memberNode: XmlNode); procedure CheckReturn(member: MemberInfo; memberNode: XmlNode); procedure CheckRemarks(member: MemberInfo; memberNode: XmlNode); procedure CheckSeeAlso(member: MemberInfo; memberNode: XmlNode); procedure CheckSummary(member: MemberInfo; memberNode: XmlNode); procedure CopyNonStandard(memberNode: XmlNode); procedure Process(member: MemberInfo); procedure PullInFromXML(member: MemberInfo; canonName: string); procedure WriteSkeleton(member: MemberInfo; canonName: string); {$ENDREGION} {$REGION 'Protected static methods'} strict protected class function RemovedDoc(outPath, unitName: string): UnitDocs; static; class function UnitDoc(outPath, unitName: string): UnitDocs; static; {$ENDREGION} {$REGION 'Protected internal methods'} protected procedure Close; {$ENDREGION} {$REGION 'Static methods'} public class procedure Generate(docs: DocOverview; outPath: string; processUnits: array of string); static; {$ENDREGION} end; {$ENDREGION} implementation {$REGION 'Helper types'} type StringArray = array of string; {$ENDREGION} {$REGION 'UnitDocs'} class constructor UnitDocs.Create; begin FUnits := Hashtable.Create(CaseInsensitiveHashCodeProvider.DefaultInvariant, CaseInsensitiveComparer.DefaultInvariant); end; constructor UnitDocs.Create(outPath, fileName: string); begin inherited Create; FUnit := Path.Combine(outPath, fileName); FWriter := XmlTextWriter.Create(StreamWriter.Create(FUnit)); XmlTextWriter(FWriter).Formatting := Formatting.Indented; XmlTextWriter(FWriter).Indentation := 2; XmlTextWriter(FWriter).IndentChar := ' '; FWriter.WriteStartDocument; FWriter.WriteComment(System.String.Format('Timestamp most recent auto generation: {0}', DateTime.UtcNow.ToString('yyyy''-''MM''-''dd HH'':''mm'':''ss ''UTC'''))); if fileName.ToLower.StartsWith('namespacedoc.') then FWriter.WriteStartElement('namespacedoc') else FWriter.WriteStartElement('members'); end; procedure UnitDocs.CheckException(member: MemberInfo; memberNode: XmlNode); begin CheckNodes(memberNode, 'exception'); end; procedure UnitDocs.CheckNode(memberNode: XmlNode; element: string; required: Boolean); begin CheckNode(memberNode, element, '', '', required); end; procedure UnitDocs.CheckNode(memberNode: XmlNode; element, attribute, attributeValue: string; required: Boolean); var xPath: string; elementNode: XmlNode; begin if (attribute <> '') and (attributeValue <> '') then xPath := System.String.Format('{0}[@{1}="{2}"]', element, attribute, attributeValue) else xPath := element; if Assigned(memberNode) then elementNode := memberNode.SelectSingleNode(xPath) else elementNode := nil; if Assigned(elementNode) then elementNode.WriteTo(FWriter) else if required then begin FWriter.WriteStartElement(element); if xPath <> element then FWriter.WriteAttributeString(attribute, attributeValue); FWriter.WriteFullEndElement; end; end; procedure UnitDocs.CheckNodes(memberNode: XmlNode; element: string); var nodeList: XmlNodeList; node: XmlNode; begin if Assigned(memberNode) then begin nodeList := memberNode.SelectNodes(element); for node in nodeList do node.WriteTo(FWriter); end; end; procedure UnitDocs.CheckParams(member: MemberInfo; memberNode: XmlNode); var i: Integer; begin for i := 0 to member.Count - 1 do begin if member[i].Location and Location.InAssembly = Location.InAssembly then CheckNode(memberNode, 'param', 'name', member[i].Name, True) end; end; procedure UnitDocs.CheckOverload(member: MemberInfo; memberNode: XmlNode); begin CheckNode(memberNode, 'overloads', False); end; procedure UnitDocs.CheckReturn(member: MemberInfo; memberNode: XmlNode); begin if member.Name.EndsWith(':P') then CheckNode(memberNode, 'value', True) else if member.HasReturnValue then CheckNode(memberNode, 'returns', True); end; procedure UnitDocs.CheckRemarks(member: MemberInfo; memberNode: XmlNode); begin CheckNode(memberNode, 'remarks', False); end; procedure UnitDocs.CheckSeeAlso(member: MemberInfo; memberNode: XmlNode); begin CheckNodes(memberNode, 'seealso'); end; procedure UnitDocs.CheckSummary(member: MemberInfo; memberNode: XmlNode); begin CheckNode(memberNode, 'summary', True); end; procedure UnitDocs.Close; begin FWriter.Close; FWriter := nil; if Path.GetExtension(FUnit) = '.generating' then begin &File.Delete(System.IO.Path.ChangeExtension(FUnit, '.xml')); &File.Move(FUnit, System.IO.Path.ChangeExtension(FUnit, '.xml')); end; end; procedure UnitDocs.CopyNonStandard(memberNode: XmlNode); var node: XmlNode; begin for node in memberNode do if (node.NodeType = XmlNodeType.Element) and (&Array.BinarySearch(&Array(StringArray.Create( 'exception', 'overloads', 'param', 'remarks', 'returns', 'seealso', 'summary', 'value')), node.Name) < 0) then node.WriteTo(FWriter); end; class procedure UnitDocs.Generate(docs: DocOverview; outPath: string; processUnits: array of string); var obj: &Object; mi: MemberInfo; ud: UnitDocs; begin &Array.Sort(&Array(processUnits), CaseInsensitiveComparer.DefaultInvariant); for obj in docs do begin mi := MemberInfo(obj); if (mi.Location <> Location.None) and (&Array.BinarySearch(&Array(processUnits), mi.UnitName, CaseInsensitiveComparer.DefaultInvariant) >= 0) then begin if mi.Location = Location.InXML then ud := RemovedDoc(outPath, mi.UnitName) else ud := UnitDoc(outPath, mi.UnitName); ud.Process(mi); end; end; for ud in FUnits.Values do ud.Close; end; procedure UnitDocs.Process(member: MemberInfo); var typeId: string; canonName: string; begin typeId := member.Name.Chars[member.Name.Length - 1]; canonName := typeId + ':' + member.Name.Substring(0, member.Name.Length - 2); FWriter.WriteStartElement('member'); FWriter.WriteAttributeString('name', canonName); if member.Location and Location.InXML = Location.InXML then PullInFromXML(member, canonName) else WriteSkeleton(member, canonName); FWriter.WriteFullEndElement; end; procedure UnitDocs.PullInFromXML(member: MemberInfo; canonName: string); var isNamespace: Boolean; orgDoc: XmlDocument; orgMemberNode: XmlNode; node: XmlNode; begin isNamespace := canonName.StartsWith('N:'); orgDoc := XmlDocument.Create; orgDoc.PreserveWhitespace := False; orgDoc.Load(member.XmlFile); if isNamespace then orgMemberNode := orgDoc.DocumentElement else orgMemberNode := orgDoc.DocumentElement.SelectSingleNode(System.String.Format('member[@name="{0}"]', &Object(canonName))); if member.Location = Location.InXML then begin for node in orgMemberNode.ChildNodes do node.WriteTo(FWriter); end else begin if isNamespace then CheckSummary(member, orgDoc.DocumentElement) else begin orgMemberNode := orgDoc.DocumentElement.SelectSingleNode(System.String.Format('member[@name="{0}"]', &Object(canonName))); if orgMemberNode.SelectSingleNode('exclude') <> nil then begin FWriter.WriteStartElement('exclude'); FWriter.WriteEndElement; end else begin CheckOverload(member, orgMemberNode); CheckSummary(member, orgMemberNode); CheckParams(member, orgMemberNode); CheckReturn(member, orgMemberNode); CheckRemarks(member, orgMemberNode); CheckException(member, orgMemberNode); CheckSeeAlso(member, orgMemberNode); CopyNonStandard(orgMemberNode); end; end; end; end; class function UnitDocs.RemovedDoc(outPath, unitName: string): UnitDocs; begin unitName := System.String.Format('{0}.removed.{1}', unitName, DateTime.UtcNow.ToString('yyyyMMddHHmmss')); Result := UnitDocs(FUnits[unitName]); if not Assigned(Result) then begin Result := UnitDocs.Create(outPath, unitName); FUnits[unitName] := Result; end; end; class function UnitDocs.UnitDoc(outPath, unitName: string): UnitDocs; begin unitName := unitName + '.generating'; Result := UnitDocs(FUnits[unitName]); if not Assigned(Result) then begin Result := UnitDocs.Create(outPath, unitName); FUnits[unitName] := Result; end; end; procedure UnitDocs.WriteSkeleton(member: MemberInfo; canonName: string); begin if member.IsOptional then FWriter.WriteComment('Optional member; you''re not required to document this member'); CheckSummary(member, nil); CheckParams(member, nil); CheckReturn(member, nil); CheckRemarks(member, nil); end; {$ENDREGION} end. --- NEW FILE: JediDoc.System.InfoClasses.pas --- {--------------------------------------------------------------------------------------------------- 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/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JediDoc.System.InfoClasses.pas, released on 2005-03-28. The Initial Developer of the Original Code is Marcel Bestebroer Portions created by Marcel Bestebroer are Copyright (C) 2004 Marcel Bestebroer All Rights Reserved. Contributor(s): You may retrieve the latest version of this file at the JEDI.NET home page, located at http://sf.net/projects/jedidotnet Known Issues: ---------------------------------------------------------------------------------------------------} // $Id: JediDoc.System.InfoClasses.pas,v 1.1 2005/03/28 12:26:16 jedi_mbe Exp $ unit JediDoc.System.InfoClasses; interface {$REGION 'uses'} uses Jedi.System.SourceVersioning, Jedi.System.Strings, System.Collections, System.Collections.Specialized, System.Globalization, System.IO, System.Reflection, System.Text; {$ENDREGION} {$REGION 'Location flags'} type [Flags] Location = (None = 0, InAssembly = 1, InXML = 2, InBoth = InAssembly + InXML); {$ENDREGION} {$REGION 'Helper types'} type ParameterInfoArray = array of ParameterInfo; StringArray = array of string; {$ENDREGION} {$REGION 'Forward declarations'} type MemberInfo = class; ParamInfo = class; {$ENDREGION} {$REGION 'Documentation overview'} DocOverview = class (MarshalByRefObject) {$REGION 'Constructor'} public constructor Create; {$ENDREGION} {$REGION 'Data'} strict private FMembers: SortedList; // of MemberInfo {$ENDREGION} {$REGION 'Protected methods'} strict protected function AddMember(typeId, name, assembly: string; loc: Location): MemberInfo; {$ENDREGION} {$REGION 'Public methods'} public function AddReflectionMember(typeId, name, assembly: string): MemberInfo; overload; function AddReflectionMember(typeId, name, assembly: string; isOptional: Boolean): MemberInfo; overload; function AddReflectionMember(typeId, name, assembly: string; isOptional: Boolean; params: StringArray): MemberInfo; overload; function AddReflectionMember(typeId, name, assembly: string; isOptional, hasRetVal: Boolean): MemberInfo; overload; function AddReflectionMember(typeId, name, assembly: string; isOptional, hasRetVal: Boolean; params: StringArray): MemberInfo; overload; function AddReflectionMember(typeId, name, assembly: string; params: StringArray): MemberInfo; overload; function AddXmlMember(memberId, xmlFile: string; params: array of string; hasExclude: Boolean): MemberInfo; function GetEnumerator: IEnumerator; overload; function GetEnumerator(assembly: string): IEnumerator; overload; function GetEnumeratorForNamespace(namespace: string): IEnumerator; procedure ProcessAssembly(path: string); {$ENDREGION} end; {$ENDREGION} {$REGION 'Info about a type or member'} MemberInfo = class (MarshalByRefObject) {$REGION 'Constructor'} protected constructor Create(typeId, name, assembly: string); {$ENDREGION} {$REGION 'Data'} strict private FAssembly: string; FHasExclude: Boolean; FHasReturnValue: Boolean; FIsOptional: Boolean; FLocation: Location; FName: string; FParamNames: ArrayList; FUnitName: string; FXmlFile: string; {$ENDREGION} {$REGION 'Protected internal methods'} protected procedure AddParameters(params: array of string; loc: Location); procedure AddReflectionParameters(params: array of string); procedure AddXmlParameters(params: array of string); procedure SetHasReturnValue(value: Boolean); procedure SetIsOptional(value: Boolean); procedure SetUnitName(unitName: string); procedure SetXmlFile(xmlFile: string; hasExclude: Boolean); procedure UpdateLocationFlags(loc: Location); {$ENDREGION} {$REGION 'Method overrides'} public function GetEnumerator: IEnumerator; function ToString: string; override; {$ENDREGION} {$REGION 'Property accessors'} public function get_Count: Integer; function get_Items(index: Integer): ParamInfo; function get_UnitName: string; {$ENDREGION} {$REGION 'Properties'} public property Assembly: string read FAssembly; property HasExclude: Boolean read FHasExclude; property HasReturnValue: Boolean read FHasReturnValue; property IsOptional: Boolean read FIsOptional; property Location: Location read FLocation; property Name: string read FName; property Count: Integer read get_Count; property Items[&index: Integer]: ParamInfo read get_Items; default; property UnitName: string read get_UnitName; property XmlFile: string read FXmlFile; {$ENDREGION} end; {$ENDREGION} {$REGION 'Information about a property or method parameter'} ParamInfo = class (MarshalByRefObject) {$REGION 'Constructor'} protected constructor Create(memberInfo: MemberInfo; name: string; loc: Location); {$ENDREGION} {$REGION 'Data'} strict private FLocation: Location; FMember: MemberInfo; FName: string; {$ENDREGION} {$REGION 'Protected internal methods'} protected procedure UpdateLocationFlags(loc: Location); {$ENDREGION} {$REGION 'Method overrides'} public function ToString: string; override; {$ENDREGION} {$REGION 'Properties'} public property Location: Location read FLocation; property Member: MemberInfo read FMember; property Name: string read FName; {$ENDREGION} end; {$ENDREGION} {$REGION 'Assembly based reflection'} AssemblyProcessor = class (MarshalByRefObject) {$REGION 'Constructor'} public constructor Create; {$ENDREGION} {$REGION 'Data'} strict protected docs: DocOverview; assembly: string; {$ENDREGION} {$REGION 'Protected methods'} strict protected procedure HandleEvent(thisEvent: EventInfo); procedure HandleField(thisField: FieldInfo); procedure HandleMethod(thisMethod: MethodBase); procedure HandleProperty(thisProperty: PropertyInfo); procedure HandleType(thisType: &Type); {$ENDREGION} {$REGION 'Protected static methods'} strict protected class function GetUnitFor(thisType: &Type): string; overload; static; class function GetUnitFor(thisMember: System.Reflection.MemberInfo): string; overload; static; class function GetUnitFromUnitMaps(thisType: &Type): string; static; class function IsEventDocumentable(thisEvent: EventInfo): Boolean; static; class function IsEventOptional(thisEvent: EventInfo): Boolean; static; class function IsFieldDocumentable(thisField: FieldInfo): Boolean; static; class function IsFieldOptional(thisField: FieldInfo): Boolean; static; class function IsMethodDocumentable(thisMethod: MethodBase; isPropertyCheck: Boolean): Boolean; static; class function IsMethodOptional(thisMethod: MethodBase): Boolean; static; class function IsPropertyDocumentable(thisProperty: PropertyInfo): Boolean; static; class function IsPropertyOptional(thisProperty: PropertyInfo): Boolean; static; class function IsTypeDocumentable(thisType: &Type): Boolean; static; class function IsTypeOptional(thisType: &Type): Boolean; static; class function IsUnitMapClass(thisType: &Type): Boolean; static; {$ENDREGION} {$REGION 'Public methods'} public procedure ParseAssembly(path: string; docs: DocOverview); {$ENDREGION} end; {$ENDREGION} {$REGION 'Naming conversions'} type Conversions = class (&Object) public class function ParamNames(params: ParameterInfoArray): StringArray; static; class function ParamString(params: ParameterInfoArray): string; static; class function MemberNameToDocName(member: System.Reflection.MemberInfo): string; static; class function MemberNameToDocType(member: System.Reflection.MemberInfo): string; static; class function TypeNameToDocName(&type: &Type): string; static; class function XmlNameToDocName(name: string): string; static; class function XmlNameToDocType(name: string): string; static; end; {$ENDREGION} implementation {$REGION 'Protected class: FilteredEnumerator'} type FilteredEnumerator = class (&Object, IEnumerator) {$REGION 'Constructor'} strict protected constructor Create(baseEnum: IEnumerator; filterForNamespace: Boolean; filterValue: string); {$ENDREGION} {$REGION 'Data'} strict private FBaseEnumerator: IEnumerator; FFilterForNamespace: Boolean; FFilterValue: string; FFilterValueAlt: string; {$ENDREGION} {$REGION 'IEnumerator methods'} strict protected function get_Current: &Object; function MoveNext: Boolean; procedure Reset; {$ENDREGION} {$REGION 'Protected methods'} strict protected function IsAllowed(info: MemberInfo): Boolean; {$ENDREGION} {$REGION 'Static methods'} public class function CreateForAssemblyFilter(baseEnum: IEnumerator; assemblyName: string): IEnumerator; static; class function CreateForNamespaceFilter(baseEnum: IEnumerator; namespace: string): IEnumerator; static; {$ENDREGION} end; {$ENDREGION} {$REGION 'AssemblyProcessor'} constructor AssemblyProcessor.Create; begin inherited Create; end; class function AssemblyProcessor.GetUnitFor(thisType: &Type): string; var attrs: array of &Object; enum: IEnumerator; pi: PropertyInfo; function IsSubClassOf(t: &Type; name: string): Boolean; begin Result := False; while not Result and Assigned(t) do begin Result := System.String.Compare(t.FullName, name, True, CultureInfo.InvariantCulture) = 0; t := t.BaseType; end; end; begin attrs := thisType.GetCustomAttributes(TypeOf(TUnitNameAttribute), False); if Length(attrs) > 0 then Result := TUnitNameAttribute(attrs[0]).UnitName else begin { We can't use SourceInfoAttribute directly, since this assembly will use a different version of the 'Jedi.System' assembly than the one that might get parsed uses (resulting in no SourceInfoAttribute being found for records/interfaces/enumerations/delegates). This problem will go away when the Jedi.System assembly would be strong-named and placed in the GAC. For now, we'll simply use reflection to get the required information. } Result := ''; attrs := thisType.GetCustomAttributes(False); enum := &Array(attrs).GetEnumerator; while enum.MoveNext and (Result = '') do if IsSubClassOf(enum.Current.GetType, TypeOf(SourceInfoAttribute).FullName) then begin pi := enum.Current.GetType.GetProperty('SourceFile'); if Assigned(pi) then Result := Path.GetFileNameWithoutExtension(System.String(pi.GetValue(enum.Current, []))); end; if (Result = '') and Assigned(thisType.DeclaringType) then Result := GetUnitFor(thisType.DeclaringType) end; if Result = '' then Result := GetUnitFromUnitMaps(thisType); if Result = '' then Result := thisType.Namespace; end; class function AssemblyProcessor.GetUnitFor(thisMember: System.Reflection.MemberInfo): string; begin Result := GetUnitFor(thisMember.DeclaringType); if Result = '' then raise Exception.Create(System.String.Format('No unit for member {0}', thisMember)); end; class function AssemblyProcessor.GetUnitFromUnitMaps(thisType: &Type): string; var unitNS: string; typeArray: array of &Type; currentType: &Type; begin Result := ''; unitNS := thisType.Namespace + '.Units'; try typeArray := thisType.Assembly.GetTypes; except on e: ReflectionTypeLoadException do typeArray := e.Types; end; for currentType in typeArray do begin if (unitNS = currentType.Namespace) and (currentType.Name = '$map$') and (currentType.GetField(thisType.Name, BindingFlags.Static or BindingFlags.Instance or BindingFlags.NonPublic or BindingFlags.Public) <> nil) then begin Result := System.String.Format('{0}.{1}', thisType.Namespace, currentType.DeclaringType.Name); Break; end; end; end; procedure AssemblyProcessor.HandleEvent(thisEvent: EventInfo); var di: MemberInfo; begin di := docs.AddReflectionMember( Conversions.MemberNameToDocType(thisEvent), Conversions.MemberNameToDocName(thisEvent), assembly, IsEventOptional(thisEvent), []); di.SetUnitName(GetUnitFor(thisEvent)); end; procedure AssemblyProcessor.HandleField(thisField: FieldInfo); var di: MemberInfo; begin di := docs.AddReflectionMember( Conversions.MemberNameToDocType(thisField), Conversions.MemberNameToDocName(thisField), assembly, IsFieldOptional(thisField), False, []); di.SetUnitName(GetUnitFor(thisField)); end; procedure AssemblyProcessor.HandleMethod(thisMethod: MethodBase); var retType: &Type; di: MemberInfo; begin if (thisMethod is MethodInfo) then retType := MethodInfo(thisMethod).ReturnType else retType := nil; di := docs.AddReflectionMember( Conversions.MemberNameToDocType(thisMethod), Conversions.MemberNameToDocName(thisMethod), assembly, IsMethodOptional(thisMethod), Assigned(retType) and not TypeOf(System.Void).IsAssignableFrom(retType), Conversions.ParamNames(thisMethod.GetParameters)); di.SetUnitName(GetUnitFor(thisMethod)); end; procedure AssemblyProcessor.HandleProperty(thisProperty: PropertyInfo); var di: MemberInfo; begin di := docs.AddReflectionMember( Conversions.MemberNameToDocType(thisProperty), Conversions.MemberNameToDocName(thisProperty), assembly, IsPropertyOptional(thisProperty), True, Conversions.ParamNames(thisProperty.GetIndexParameters)); di.SetUnitName(GetUnitFor(thisProperty)); end; procedure AssemblyProcessor.HandleType(thisType: &Type); var di: MemberInfo; mi: System.Reflection.MemberInfo; begin // Asserts the namespace item exists. di := docs.AddReflectionMember('N:', thisType.Namespace, assembly); di.SetUnitName('namespaceDoc.' + thisType.Namespace); // Asserts the type exists. di := docs.AddReflectionMember('T:', Conversions.TypeNameToDocName(thisType), assembly, IsTypeOptional(thisType)); di.SetUnitName(GetUnitFor(thisType)); for mi in thisType.GetMembers(BindingFlags.Static or BindingFlags.Instance or BindingFlags.Public or BindingFlags.NonPublic or BindingFlags.DeclaredOnly) do begin if (mi is EventInfo) and IsEventDocumentable(EventInfo(mi)) then HandleEvent(EventInfo(mi)) else if (mi is FieldInfo) and IsFieldDocumentable(FieldInfo(mi)) then HandleField(FieldInfo(mi)) else if (mi is MethodBase) and IsMethodDocumentable(MethodBase(mi), False) then HandleMethod(MethodBase(mi)) else if (mi is PropertyInfo) and IsPropertyDocumentable(PropertyInfo(mi)) then HandleProperty(PropertyInfo(mi)); end; end; class function AssemblyProcessor.IsEventDocumentable(thisEvent: EventInfo): Boolean; var accessMethod: MethodInfo; begin accessMethod := thisEvent.GetAddMethod(True); if not Assigned(accessMethod) then accessMethod := thisEvent.GetRemoveMethod(True); Result := thisEvent.IsSpecialName and Assigned(accessMethod) and IsMethodDocumentable(accessMethod, True); end; class function AssemblyProcessor.IsEventOptional(thisEvent: EventInfo): Boolean; var accessMethod: MethodInfo; begin accessMethod := thisEvent.GetAddMethod(True); if not Assigned(accessMethod) then accessMethod := thisEvent.GetRemoveMethod(True); Result := Assigned(accessMethod) and IsMethodOptional(accessMethod); end; class function AssemblyProcessor.IsFieldDocumentable(thisField: FieldInfo): Boolean; begin Result := not thisField.IsSpecialName and (thisField.IsPublic or thisField.IsFamily or thisField.IsFamilyOrAssembly); end; class function AssemblyProcessor.IsFieldOptional(thisField: FieldInfo): Boolean; begin Result := False; end; class function AssemblyProcessor.IsMethodDocumentable(thisMethod: MethodBase; isPropertyCheck: Boolean): Boolean; var methodArray: array of MethodInfo; i: Integer; begin // ignore any method declared by TObjectHelper methodArray := TypeOf(TObjectHelper).GetMethods(BindingFlags.Static or BindingFlags.Public); i := High(methodArray); while (i >= 0) and (methodArray[i].Name <> thisMethod.Name) do Dec(i); Result := i < 0; if Result then Result := not thisMethod.IsPrivate and not thisMethod.IsAssembly and (thisMethod.Name <> '@__CloneHelper__') and (thisMethod.Name <> '__Initialize__'); if Result and not isPropertyCheck and thisMethod.IsSpecialName then { only if it's a constructor or a class operator } Result := thisMethod.IsConstructor or thisMethod.Name.StartsWith('op_'); end; class function AssemblyProcessor.IsMethodOptional(thisMethod: MethodBase): Boolean; begin Result := // optional type => optional method IsTypeOptional(thisMethod.DeclaringType) or // if it the method is inherited from a parent class the method is also optional ((thisMethod is MethodInfo) and (MethodInfo(thisMethod).GetBaseDefinition.DeclaringType <> thisMethod.DeclaringType)); end; class function AssemblyProcessor.IsPropertyDocumentable(thisProperty: PropertyInfo): Boolean; var accessMethod: MethodInfo; begin accessMethod := thisProperty.GetGetMethod(True); if not Assigned(accessMethod) then accessMethod := thisProperty.GetSetMethod(True); Result := not thisProperty.IsSpecialName and Assigned(accessMethod) and IsMethodDocumentable(accessMethod, True); end; class function AssemblyProcessor.IsPropertyOptional(thisProperty: PropertyInfo): Boolean; var accessMethod: MethodInfo; begin accessMethod := thisProperty.GetGetMethod(True); if not Assigned(accessMethod) then accessMethod := thisProperty.GetSetMethod(True); Result := Assigned(accessMethod) and IsMethodOptional(accessMethod); end; class function AssemblyProcessor.IsTypeDocumentable(thisType: &Type): Boolean; begin // Ignore all the Delphi meta classes and implementation declared types. if thisType.FullName.IndexOfAny(['@', '$']) > -1 then Result := False else if thisType.DeclaringType = nil then Result := thisType.IsPublic else Result := (thisType.IsNestedPublic or thisType.IsNestedFamORAssem) and IsTypeDocumentable(thisType.DeclaringType); // Ignore the Unit type (Delphi 8) or the *.Units namespace (Delphi 2005) if it is indeed Delphi based (look for // the initalization section of the unit in both cases; must be a class static public method.) if Result then begin if thisType.Name = 'Unit' then Result := not Assigned(thisType.GetMethod(thisType.Namespace, BindingFlags.Static or BindingFlags.&Public)) else if thisType.Namespace.EndsWith('.Units') then Result := not Assigned(thisType.GetMethod( thisType.Namespace.Substring(0, thisType.Namespace.Length - 5) + thisType.Name, BindingFlags.Static or BindingFlags.&Public)); end; if Result and Assigned(thisType.BaseType) then // better make sure the base type hierarchy if documentable too. Result := IsTypeDocumentable(thisType.BaseType); end; class function AssemblyProcessor.IsTypeOptional(thisType: &Type): Boolean; begin if thisType.DeclaringType = nil then Result := not thisType.IsPublic else Result := not thisType.IsNestedPublic or thisType.DeclaringType.IsSealed; end; class function AssemblyProcessor.IsUnitMapClass(thisType: &Type): Boolean; begin Result := thisType.IsNestedPrivate and (thisType.Name = '$map$') and thisType.Namespace.EndsWith('.Units'); end; procedure AssemblyProcessor.ParseAssembly(path: string; docs: DocOverview); var assy: System.Reflection.Assembly; thisType: &Type; begin assy := System.Reflection.Assembly.LoadFrom(path); Self.docs := docs; assembly := assy.GetName.Name; for thisType in &Array(assy.GetExportedTypes) do if IsTypeDocumentable(thisType) then HandleType(thisType) end; {$ENDREGION} {$REGION 'Conversions'} class function Conversions.MemberNameToDocName(member: System.Reflection.MemberInfo): string; var sb: StringBuilder; params: array of ParameterInfo; begin sb := StringBuilder.Create; params := nil; if member is PropertyInfo then params := PropertyInfo(member).GetIndexParameters else if member is MethodBase then params := MethodBase(member).GetParameters; sb.Append(TypeNameToDocName(member.ReflectedType)); sb.Append('.'); sb.Append(member.Name.Replace('.', '#')); if Assigned(params) then sb.Append(ParamString(params)); if (member is MethodInfo) and ((member.Name.ToLower = 'op_implicit') or (member.Name.ToLower = 'op_explicit')) then begin sb.Append('~'); sb.Append(TypeNameToDocName(MethodInfo(member).ReturnType)); end; Result := sb.ToString; end; class function Conversions.MemberNameToDocType(member: System.Reflection.MemberInfo): string; begin if member is PropertyInfo then Result := 'P' else if member is MethodBase then Result := 'M' else if member is EventInfo then Result := 'E' else if member is FieldInfo then Result := 'F' else Result := '!'; end; class function Conversions.ParamNames(params: array of ParameterInfo): StringArray; var i: Integer; begin if Assigned(params) then begin Result := new (StringArray, &Array(params).Length); for i := 0 to &Array(params).Length - 1 do Result[i] := params[i].Name; end else Result := new (StringArray, 0); end; class function Conversions.ParamString(params: array of ParameterInfo): string; var sb: StringBuilder; i: Integer; begin sb := StringBuilder.Create; for i := Low(params) to High(params) do begin sb.Append(TypeNameToDocName(params[i].ParameterType).Replace('&', '@')); sb.Append(','); end; if sb.Length > 0 then begin sb.Insert(0, '('); sb.Remove(sb.Length - 1, 1); sb.Append(')'); end; Result := sb.ToString; end; class function Conversions.TypeNameToDocName(&type: &Type): string; begin Result := &type.FullName.Replace('+', '.'); end; class function Conversions.XmlNameToDocName(name: string): string; begin Result := name.Substring(2); if name.EndsWith('.NamespaceDoc') then Result := Result.Substring(0, Result.Length - 'NamespaceDoc'.Length); end; class function Conversions.XmlNameToDocType(name: string): string; begin if name.EndsWith('.NamespaceDoc') then Result := 'N:' else Result := name.ToUpper.Substring(0, 2); end; {$ENDREGION} {$REGION 'DocOVerview'} constructor DocOverview.Create; begin inherited Create; FMembers := SortedList.Create(CaseInsensitiveComparer.DefaultInvariant); end; function DocOverview.AddMember(typeId, name, assembly: string; loc: Location): MemberInfo; var keyName: string; idx: Integer; begin keyName := name + ' ' + typeId.ToUpper.Chars[0]; idx := FMembers.IndexOfKey(keyName); if idx < 0 then begin Result := MemberInfo.Create(typeId, name, assembly); FMembers.Add(keyName, Result); end else Result := MemberInfo(FMembers[keyName]); Result.UpdateLocationFlags(loc); end; function DocOverview.AddReflectionMember(typeId, name, assembly: string): MemberInfo; begin Result := AddMember(typeId, name, assembly, Location.InAssembly); end; function DocOverview.AddReflectionMember(typeId, name, assembly: string; isOptional: Boolean): MemberInfo; begin Result := AddReflectionMember(typeId, name, assembly); Result.SetIsOptional(isOptional); end; function DocOverview.AddReflectionMember(typeId, name, assembly: string; isOptional: Boolean; params: array of string): MemberInfo; begin Result := AddReflectionMember(typeId, name, assembly); Result.SetIsOptional(isOptional); Result.AddReflectionParameters(params); end; function DocOverview.AddReflectionMember(typeId, name, assembly: string; isOptional, hasRetVal: Boolean): MemberInfo; begin Result := AddReflectionMember(typeId, name, assembly); Result.SetIsOptional(isOptional); end; function DocOverview.AddReflectionMember(typeId, name, assembly: string; isOptional, hasRetVal: Boolean; params: array of string): MemberInfo; begin Result := AddReflectionMember(typeId, name, assembly); Result.SetIsOptional(isOptional); Result.SetHasReturnValue(hasRetVal); Result.AddReflectionParameters(params); end; function DocOverview.AddReflectionMember(typeId, name, assembly: string; params: array of string): MemberInfo; begin Result := AddReflectionMember(typeId, name, assembly); Result.AddReflectionParameters(params); end; function DocOverview.AddXmlMember(memberId, xmlFile: string; params: array of string; hasExclude: Boolean): MemberInfo; begin Result := AddMember(Conversions.XmlNameToDocType(memberId), Conversions.XmlNameToDocName(memberId), '', Location.InXml); Result.SetXmlFile(xmlFile, hasExclude); if Result.UnitName = '' then Result.SetUnitName(StringUtils.BeforeLast(StringUtils.AfterLastAnyOf(xmlFile, '/\'.ToCharArray), '.')); Result.AddXmlParameters(params); end; function DocOverview.GetEnumerator: IEnumerator; begin Result := FMembers.Values.GetEnumerator; end; function DocOverview.GetEnumerator(assembly: string): IEnumerator; begin Result := FilteredEnumerator.CreateForAssemblyFilter(GetEnumerator, assembly); end; function DocOverview.GetEnumeratorForNamespace(namespace: string): IEnumerator; begin Result := FilteredEnumerator.CreateForNamespaceFilter(GetEnumerator, namespace); end; procedure DocOverview.ProcessAssembly(path: string); var domainSetup: AppDomainSetup; loaderDomain: AppDomain; processor: AssemblyProcessor; begin domainSetup := AppDomainSetup.Create; domainSetup.ApplicationBase := AppDomain.CurrentDomain.BaseDirectory; domainSetup.PrivateBinPath := AppDomain.CurrentDomain.BaseDirectory; domainSetup.ApplicationName := 'Loader'; domainSetup.ShadowCopyFiles := 'true'; loaderDomain := AppDomain.CreateDomain('Assembly Loader', nil, domainSetup); processor := AssemblyProcessor(loaderDomain.CreateInstanceFromAndUnwrap(GetType.Assembly.Location, TypeOf(AssemblyProcessor).FullName)); try processor := AssemblyProcessor.Create; processor.ParseAssembly(path, Self); finally AppDomain.Unload(loaderDomain); loaderDomain := nil; end; end; {$ENDREGION} {$REGION 'FilteredEnumerator'} constructor FilteredEnumerator.Create(baseEnum: IEnumerator; filterForNamespace: Boolean; filterValue: string); begin inherited Create; FBaseEnumerator := baseEnum; FFilterForNamespace := filterForNamespace; if filterForNamespace then begin if not filterValue.EndsWith('.') then FFilterValue := filterValue + '.' else FFilterValue := filterValue; FFilterValueAlt := FFilterValue.Substring(0, FFilterValue.Length - 1) + ' N'; end else FFilterValue := filterValue; end; class function FilteredEnumerator.CreateForAssemblyFilter(baseEnum: IEnumerator; assemblyName: string): IEnumerator; begin Result := FilteredEnumerator.Create(baseEnum, False, assemblyName); end; class function FilteredEnumerator.CreateForNamespaceFilter(baseEnum: IEnumerator; namespace: string): IEnumerator; begin Result := FilteredEnumerator.Create(baseEnum, True, namespace); end; function FilteredEnumerator.get_Current: &Object; begin Result := FBaseEnumerator.Current; end; function FilteredEnumerator.IsAllowed(info: MemberInfo): Boolean; begin if FFilterForNamespace then Result := (System.String.Compare(FFilterValue, info.Name.Substring(0, FFilterValue.Length), True, CultureInfo.InvariantCulture) = 0) or (System.String.Compare(FFilterValueAlt, info.Name.Substring(0, FFilterValueAlt.Length), True, CultureInfo.InvariantCulture) = 0) else Result := System.String.Compare(FFilterValue, info.Assembly, True, CultureInfo.InvariantCulture) = 0; end; function FilteredEnumerator.MoveNext: Boolean; begin repeat Result := FBaseEnumerator.MoveNext; until not Result or IsAllowed(MemberInfo(FBaseEnumerator.Current)); end; procedure FilteredEnumerator.Reset; begin FBaseEnumerator.Reset; end; {$ENDREGION} {$REGION 'MemberInfo'} constructor MemberInfo.Create(typeId, name, assembly: string); begin inherited Create; FAssembly := assembly; FName := name + ':' + typeId.SubString(0, 1).ToUpper; FLocation := JediDoc.System.InfoClasses.Location.None; FParamNames := ArrayList.Create; end; procedure MemberInfo.AddParameters(params: array of string; loc: Location); var thisParamName: string; hasFound: Boolean; thisParamInfo: ParamInfo; begin for thisParamName in params do begin hasFound := False; for thisParamInfo in FParamNames do begin if System.String.Compare(thisParamName, thisParamInfo.Name, True, CultureInfo.InvariantCulture) = 0 then begin thisParamInfo.UpdateLocationFlags(JediDoc.System.InfoClasses.Location.InXML); hasFound := True; Break; end; end; if not hasFound then FParamNames.Add(ParamInfo.Create(Self, thisParamName, loc)); end; end; procedure MemberInfo.AddReflectionParameters(params: array of string); begin AddParameters(params, JediDoc.System.InfoClasses.Location.InAssembly); end; procedure MemberInfo.AddXmlParameters(params: array of string); begin AddParameters(params, JediDoc.System.InfoClasses.Location.InXml); end; function MemberInfo.get_Count: Integer; begin Result := FParamNames.Count; end; function MemberInfo.get_Items(index: Integer): ParamInfo; begin Result := ParamInfo(FParamNames[index]); end; function MemberInfo.get_UnitName: string; begin Result := FUnitName end; function MemberInfo.GetEnumerator: IEnumerator; begin Result := FParamNames.GetEnumerator; end; procedure MemberInfo.SetHasReturnValue(value: Boolean); begin FHasReturnValue := value; end; procedure MemberInfo.SetIsOptional(value: Boolean); begin FIsOptional := value; end; procedure MemberInfo.SetUnitName(unitName: string); begin FUnitName := unitName; end; procedure MemberInfo.SetXmlFile(xmlFile: string; hasExclude: Boolean); begin FXmlFile := xmlFile; FHasExclude := hasExclude; end; function MemberInfo.ToString: string; begin Result := FName; end; procedure MemberInfo.UpdateLocationFlags(loc: Location); begin FLocation := FLocation or loc; end; {$ENDREGION} {$REGION 'ParamInfo'} constructor ParamInfo.Create(memberInfo: MemberInfo; name: string; loc: Location); begin inherited Create; FMember := memberInfo; FName := name; FLocation := loc; end; function ParamInfo.ToString: string; begin Result := FMember.ToString + ' [param ' + FName + ']'; end; procedure ParamInfo.UpdateLocationFlags(loc: Location); begin FLocation := FLocation or loc; end; {$ENDREGION} end. --- NEW FILE: JediDoc.System.Console.pas --- {--------------------------------------------------------------------------------------------------- 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/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JediDoc.System.Console.pas, released on 2005-03-28. The Initial Developer of the Original Code is Marcel Bestebroer Portions created by Marcel Bestebroer are Copyright (C) 2004 Marcel Bestebroer All Rights Reserved. Contributor(s): You may retrieve the latest version of this file at the JEDI.NET home page, located at http://sf.net/projects/jedidotnet Known Issues: ---------------------------------------------------------------------------------------------------} // $Id: JediDoc.System.Console.pas,v 1.1 2005/03/28 12:26:16 jedi_mbe Exp $ unit JediDoc.System.Console; interface {$REGION 'uses'} uses Jedi.System.CommandLine, JediDoc.System.Project, System.Diagnostics, System.IO; {$ENDREGION} {$REGION 'Settings class'} type Settings = class {$REGION 'Constructor'} strict protected constructor Create; {$ENDREGION} {$REGION 'ExecutionTypes enumeration'} public type ExecutionTypes = (SaveProject = 0, Analyse = 1, UnitGeneration = 2, AssemblyGeneration = 4); {$ENDREGION} {$REGION 'Data'} strict private class var FArgumentDetected: Boolean; class var FExecutionType: ExecutionTypes; class var FHelpShown: Boolean; class var FProjectFile: string; class var FProjectContents: Project; class var FQuiet: Boolean; {$ENDREGION} {$REGION 'Protected static methods'} strict protected [CommandLineArgument(CaseSensitive = False, Prefix = '/', Name = '?')] [CommandLineArgument(CaseSensitive = False, Prefix = '--', Name = 'help')] class procedure HelpSwitch(match, commandLine: string; var index: Integer); static; {$ENDREGION} {$REGION 'Protected property accessors'} strict protected class function get_Analyse: Boolean; static; class function get_AssemblyGeneration: Boolean; static; class function get_UnitGeneration: Boolean; static; class procedure set_Analyse(value: Boolean); static; class procedure set_AssemblyGeneration(value: Boolean); static; class procedure set_UnitGeneration(value: Boolean); static; {$ENDREGION} {$REGION 'Protected properties'} strict protected [CommandLineArgument(CaseSensitive = False, Prefix = '--', Name = 'analyse')] class property Analyse: Boolean read get_Analyse write set_Analyse; [CommandLineArgument(CaseSensitive = False, Prefix = '--', Name = 'generate:assemblydocs')] class property AssemblyGeneration: Boolean read get_AssemblyGeneration write set_AssemblyGeneration; [CommandLineArgument(CaseSensitive = False, Prefix = '--', Name = 'generate:unitdocs')] class property UnitGeneration: Boolean read get_UnitGeneration write set_UnitGeneration; {$ENDREGION} {$REGION 'Static methods'} public class function ArgumentDetected: Boolean; static; class function HelpShown: Boolean; static; class procedure ParseCommandLine; static; class procedure ShowHelp; static; {$ENDREGION} {$REGION 'Properties'} public class property ExecutionType: ExecutionTypes read FExecutionType; class property ProjectContents: Project read FProjectContents; class property ProjectFile: string read FProjectFile; [CommandLineArgument(CaseSensitive = False, Prefix = '-', Name = 'q')] class property Quiet: Boolean read FQuiet write FQuiet; {$ENDREGION} end; {$ENDREGION} implementation {$REGION 'Settings'} constructor Settings.Create; begin inherited Create; end; class function Settings.ArgumentDetected: Boolean; begin Result := FArgumentDetected or (FProjectContents.Assemblies.Count > 0) or (FProjectContents.XmlFiles.Count > 0); end; class function Settings.HelpShown: Boolean; begin Result := FHelpShown; end; class function Settings.get_Analyse: Boolean; begin Result := (FExecutionType and ExecutionTypes.Analyse) = ExecutionTypes.Analyse; end; class function Settings.get_AssemblyGeneration: Boolean; begin Result := (FExecutionType and ExecutionTypes.AssemblyGeneration) = ExecutionTypes.AssemblyGeneration; end; class function Settings.get_UnitGeneration: Boolean; begin Result := (FExecutionType and ExecutionTypes.UnitGeneration) = ExecutionTypes.UnitGeneration; end; class procedure Settings.set_Analyse(value: Boolean); begin FArgumentDetected := True; if value then FExecutionType := FExecutionType or ExecutionTypes.Analyse else FExecutionType := (FExecutionType or ExecutionTypes.Analyse) xor ExecutionTypes.Analyse; end; class procedure Settings.set_AssemblyGeneration(value: Boolean); begin FArgumentDetected := True; if value then FExecutionType := FExecutionType or ExecutionTypes.AssemblyGeneration else FExecutionType := (FExecutionType or ExecutionTypes.AssemblyGeneration) xor ExecutionTypes.AssemblyGeneration; end; class procedure Settings.set_UnitGeneration(value: Boolean); begin FArgumentDetected := True; if value then FExecutionType := FExecutionType or ExecutionTypes.UnitGeneration else FExecutionType := (FExecutionType or ExecutionTypes.UnitGeneration) xor ExecutionTypes.UnitGeneration; end; class procedure Settings.HelpSwitch(match, commandLine: string; var index: Integer); begin FArgumentDetected := True; FHelpShown := True; index := commandLine.Length; // stop further parsing ShowHelp; end; class procedure Settings.ParseCommandLine; var notSwitches: array of string; begin FProjectContents := Project.Create; try notSwitches := CommandLine.Parse([TypeOf(Settings), FProjectContents.Assemblies, FProjectContents.XmlFiles], '@'); if &Array(notSwitches).Length > 1 then begin Console.WriteLine('ERROR: Commandline parsing error:'); Console.WriteLine('More than project filename specified.'); Halt(100); end; if &Array(notSwitches).Length = 1 then FProjectFile := notSwitches[0]; if ArgumentDetected and not HelpShown and (ExecutionType = ExecutionTypes.SaveProject) and (ProjectFile = '') then begin Console.WriteLine('ERROR: Commandline parsing error:'); Console.WriteLine('No project filename specified to save to.'); Halt(100); end; //TODO: loading project file if one was specified. except on e: CommandLineException do begin Console.WriteLine('ERROR: Commandline parsing error:'); Console.WriteLine(e.Message); Halt(100); end else raise; end; end; class procedure Settings.ShowHelp; begin Console.Write('Usage: '); Console.Write(Path.GetFileNameWithoutExtension(Process.GetCurrentProcess.MainModule.ModuleName)); Console.WriteLine(' [options] project'); Console.WriteLine; Console.WriteLine('Valid options:'); Console.WriteLine; Console.WriteLine(' -a[ssemblypath]:<path of assembly/assemblies>'); Console.WriteLine(' Adds the specified assembly or assemblies to the project. You can use'); Console.WriteLine(' wild-cards in the file name.'); Console.WriteLine(' -x[mlpath]:<path of xml file/files>'); Console.WriteLine(' Adds the specified xml file or files to the project. You can use wild-'); Console.WriteLine(' cards in the file name.'); Console.WriteLine(' -q'); Console.WriteLine(' Use quiet mode (no output other than errors and warnings).'); Console.WriteLine(' /? or --help'); Console.WriteLine(' Show this help page (also shown when no arguments were specified).'); Console.WriteLine(' --analyse'); Console.WriteLine(' Analyses the project for mismatches in documentation/assemblies.'); Console.WriteLine(' Will always result in output, regardless of -q option.'); Console.WriteLine(' --generate:unitdocs'); Console.WriteLine(' Generates/updates unit based XML files from the parsed assemblies.'); Console.WriteLine(' --generate:assemblydocs'); Console.WriteLine(' Generates/updates assembly based XML files from the parsed assemblies and'); Console.WriteLine(' xml files. <include> tags will be processed.'); Console.WriteLine; Console.WriteLine('Notes:'); Console.WriteLine(' The -a and -x options affect the default project. When a project file name is'); Console.WriteLine(' specified, those settings will be ignored.'); Console.WriteLine; Console.WriteLine(' If neither the analyse nor either of the generate options were specified, the'); Console.WriteLine(' default project as build through the -a and -x options will be saved as the'); Console.WriteLine(' specified project file (if no file name is specied an error message is'); Console.WriteLine(' displayed).'); Console.WriteLine; Console.WriteLine(' Instead of specifying everything on the command line, you can put all'); Console.WriteLine(' arguments in a file and use that file through the @<file> option. You can'); Console.WriteLine(' freely mix the usage of response files and arguments.'); Console.WriteLine; end; {$ENDREGION} end. --- NEW FILE: JediDoc.System.Analysis.pas --- {--------------------------------------------------------------------------------------------------- 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/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JediDoc.System.Analysis.pas, released on 2005-03-28. The Initial Developer of the Original Code is Marcel Bestebroer Portions created by Marcel Bestebroer are Copyright (C) 2004 Marcel Bestebroer All Rights Reserved. Contributor(s): You may retrieve the latest version of this file at the JEDI.NET home page, located at http://sf.net/projects/jedidotnet Known Issues: ---------------------------------------------------------------------------------------------------} // $Id: JediDoc.System.Analysis.pas,v 1.1 2005/03/28 12:26:16 jedi_mbe Exp $ unit JediDoc.System.Analysis; interface {$REGION 'uses'} uses System.Collections, System.IO, Jedi.System.Strings, JediDoc.System.InfoClasses; {$ENDREGION} {$REGION 'Helper types'} type StringArray = array of string; {$ENDREGION} {$REGION 'Documentation analyzer'} type DocAnalyzer = class {$REGION 'Private static methods'} strict private class procedure GetMembersInLocation(overview: DocOverview; onlyIn: Location; includeParams: Boolean; list: ArrayList); static; class procedure GetParametersInLocation(memberInfo: MemberInfo; onlyIn: Location; list: ArrayList); static; {$ENDREGION} {$REGION 'Static methods'} public class function GetAssemblyOnlyElements(overview: DocOverview): ArrayList; static; class function GetDocumentOnlyElements(overview: DocOverview): ArrayList; static; class function GetChangedUnits(overview: DocOverview): StringArray; static; {$ENDREGION} end; {$ENDREGION} implementation {$REGION 'DocAnalyzer'} class function DocAnalyzer.GetAssemblyOnlyElements(overview: DocOverview): ArrayList; begin Result := ArrayList.Create; GetMembersInLocation(overview, Location.InAssembly, True, Result); end; class function DocAnalyzer.GetDocumentOnlyElements(overview: DocOverview): ArrayList; begin Result := ArrayList.Create; GetMembersInLocation(overview, Location.InXML, True, Result); end; class procedure DocAnalyzer.GetMembersInLocation(overview: DocOverview; onlyIn: Location; includeParams: Boolean; list: ArrayList); var obj: &Object; mi: MemberInfo; begin for obj in overview do begin mi := MemberInfo(obj); if mi.Location = onlyIn then list.Add(mi) else if includeParams and not mi.HasExclude then GetParametersInLocation(mi, onlyIn, list); end; end; class procedure DocAnalyzer.GetParametersInLocation(memberInfo: MemberInfo; onlyIn: Location; list: ArrayList); var obj: &Object; pi: ParamInfo; begin for obj in memberInfo do begin pi := ParamInfo(obj); if (pi.Location = onlyIn) then list.Add(pi); end; end; class function DocAnalyzer.GetChangedUnits(overview: DocOverview): StringArray; var al: ArrayList; obj: &Object; mi: MemberInfo; idx: Integer; obj2: &Object; pi: ParamInfo; begin al := ArrayList.Create; for obj in overview do begin mi := MemberInfo(obj); if (mi.Location <> Location.InBoth) or (mi.UnitName <> Path.GetFileNameWithoutExtension(mi.XmlFile)) then begin idx := al.BinarySearch(mi.UnitName, CaseInsensitiveComparer.DefaultInvariant); if idx < 0 then al.Insert(not idx, mi.UnitName); end else if not mi.HasExclude then for obj2 in mi do begin pi := ParamInfo(obj2); if pi.Location <> Location.InBoth then begin idx := al.BinarySearch(mi.UnitName, CaseInsensitiveComparer.DefaultInvariant); if idx < 0 then al.Insert(not idx, mi.UnitName); end; end; end; Result := StringArray(al.ToArray(TypeOf(System.String))); end; {$ENDREGION} end. --- NEW FILE: JediDoc.System.Xml.Reflection.pas --- {--------------------------------------------------------------------------------------------------- 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/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JediDoc.System.Xml.Reflection.pas, released on 2005-03-28. The Initial Developer of the Original Code is Marcel Bestebroer Portions created by Marcel Bestebroer are Copyright (C) 2004 Marcel Bestebroer All Rights Reserved. Contributor(s): You may retrieve the latest version of this file at the JEDI.NET home page, located at http://sf.net/projects/jedidotnet Known Issues: ---------------------------------------------------------------------------------------------------} // $Id: JediDoc.System.Xml.Reflection.pas,v 1.1 2005/03/28 12:26:16 jedi_mbe Exp $ unit JediDoc.System.Xml.Reflection; interface {$REGION 'uses'} uses System.Collections, System.IO, System.Xml, JediDoc.System.InfoClasses; {$ENDREGION} {$REGION 'Xml-based reflection'} type XmlReflector = class {$REGION 'Private static methods'} strict private class function GetParameterNames(doc: XmlDocument): StringArray; static; class function GetParameters(doc: XmlDocument): XmlNodeList; static; class procedure ProcessXmlMember(reader: XmlReader; filePath: string; docs: DocOverview; fileList: HashTable); static; {$ENDREGION} {$REGION 'Static methods'} public class procedure ProcessXml(path: string; docs: DocOverview); static; {$ENDREGION} end; {$ENDREGION} {$REGION 'Utilities (include processing)'} XmlUtils = class {$REGION 'Private static methods'} strict private class function GetIncludedNodes(filename, xpath: string; fileList: HashTable): XmlNodeList; static; {$ENDREGION} {$REGION 'Static methods'} public class procedure ExpandIncludeNodes(doc: XmlDocument; fileList: HashTable); static; {$ENDREGION} end; {$ENDREGION} implementation {$AUTOBOX ON} {$REGION 'XmlReflector'} class function XmlReflector.GetParameterNames(doc: XmlDocument): StringArray; var paramList: XmlNodeList; nameList: ArrayList; node: XmlNode; begin paramList := GetParameters(doc); nameList := ArrayList.Create(paramList.Count); for node in paramList do nameList.Add(node.Attributes['name'].Value); Result := StringArray(nameList.ToArray(TypeOf(System.String))); end; class function XmlReflector.GetParameters(doc: XmlDocument): XmlNodeList; begin Result := doc.GetElementsByTagName('param'); end; class procedure XmlReflector.ProcessXml(path: string; docs: DocOverview); var fileList: HashTable; savedDirectory: string; xr: XmlReader; begin savedDirectory := System.Environment.CurrentDirectory; xr := XmlTextReader.Create(StreamReader.Create(path)); try System.Environment.CurrentDirectory := System.IO.Path.GetDirectoryName(System.IO.Path.GetFullPath(path)); fileList := Hashtable.Create(CaseInsensitiveHashCodeProvider.DefaultInvariant, CaseInsensitiveComparer.DefaultInvariant); if System.IO.Path.GetFileNameWithoutExtension(path).ToLower.StartsWith('namespacedoc.') then begin docs.AddXmlMember('N:' + System.IO.path.GetFileNameWithoutExtension(path).Substring('namespacedoc.'.Length), path, [], False); end else begin if xr.MoveToContent = XmlNodeType.Element then begin xr.ReadStartElement('members'); while xr.IsStartElement do begin if xr.IsStartElement('member') then ProcessXmlMember(xr, path, docs, fileList) else xr.Skip; end; xr.ReadEndElement; end; end; finally xr.Close; // release the file System.Environment.CurrentDirectory := savedDirectory; end; end; class procedure XmlReflector.ProcessXmlMember(reader: XmlReader; filePath: string; docs: DocOverview; fileList: HashTable); var line: Integer; pos: Integer; memberId: string; tmpXml: string; doc: XmlDocument; params: array of string; begin if reader is XmlTextReader then begin line := XmlTextRea... [truncated message content] |