Browse files

Merge pull request #8 from alexvictoor/master

LCOM4 support
  • Loading branch information...
2 parents 4920615 + 51e89be commit 74933f3b1ca5aa8dba12b459b791f3a3a9f8c325 @grozeille committed Feb 2, 2013
View
22 DependencyParser.Test/DependencyParser.Test.csproj
@@ -34,8 +34,12 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="Mono.Cecil">
+ <HintPath>..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll</HintPath>
+ </Reference>
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NUnit.2.6.2\lib\nunit.framework.dll</HintPath>
@@ -49,15 +53,14 @@
</ItemGroup>
<ItemGroup>
<Compile Include="DependencyParserTest.cs" />
+ <Compile Include="Lcom4AnalyzerTest.cs" />
+ <Compile Include="Lcom4WriterTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\DependencyParser\DependencyParser.csproj">
- <Project>{AE47EF64-9F61-463A-9088-79907968A1FE}</Project>
- <Name>DependencyParser</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
+ <Content Include="lcom4-expected.xml">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
<Content Include="testdata\Example.Core.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
@@ -66,11 +69,18 @@
</Content>
<Content Include="testdata\test.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ <SubType>Designer</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\DependencyParser\DependencyParser.csproj">
+ <Project>{AE47EF64-9F61-463A-9088-79907968A1FE}</Project>
+ <Name>DependencyParser</Name>
+ </ProjectReference>
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
View
444 DependencyParser.Test/Lcom4AnalyzerTest.cs
@@ -0,0 +1,444 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Mono.Cecil;
+using NUnit.Framework;
+
+namespace DependencyParser.Test {
+
+
+ [NUnit.Framework.TestFixture]
+ public class Lcom4AnalyzerTest {
+ [Test]
+ public void Should_Find_One_Block_On_A_Simple_Class()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.SimpleClass"));
+ Assert.AreEqual(1, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Find_One_Block_On_A_Class_With_Strong_Cohesion()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.ClassWithManyMethodsAndStrongCohesion"));
+ Assert.AreEqual(1, blocks.Count);
+ Assert.AreEqual(5, blocks.ElementAt(0).Count);
+ }
+
+ [Test]
+ public void Should_Ignore_Simple_Accessors()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.ClassWithProperties"));
+ Assert.AreEqual(0, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Take_In_Account_Calls_To_Simple_Accessors()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.ClassWithCallsToAccessors"));
+ Assert.AreEqual(1, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Take_In_Account_Complex_Accessors()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.ClassWithComplexProperties"));
+ Assert.AreEqual(2, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Ignore_Abstract_Methods()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.AbstractClass"));
+ Assert.AreEqual(1, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Take_In_Account_Calls_To_Abstract_Methods()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.AbstractTemplateClass"));
+ Assert.AreEqual(1, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Ignore_Constructors()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.SimpleClassWithCtr"));
+ Assert.AreEqual(2, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Not_Fail_On_Empty_Interface()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.IEmpty"));
+ Assert.AreEqual(0, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Not_Fail_On_Empty_Class()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.EmptyClass"));
+ Assert.AreEqual(0, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Not_Take_In_Account_Dispose_Methods()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.SimpleDisposableClass"));
+ Assert.AreEqual(1, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Not_Take_In_Account_Equals_Methods()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.SimpleClassWithEquals"));
+ Assert.AreEqual(2, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Not_Take_In_Account_ToString_Methods()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.SimpleClassWithToString"));
+ Assert.AreEqual(2, blocks.Count);
+ }
+
+ [Test]
+ public void Should_Not_Take_In_Account_Methods_From_Wrapped_Objects()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.SimpleClassWithDelegation"));
+ Assert.AreEqual(1, blocks.Count);
+ Assert.AreEqual(3, blocks.ElementAt(0).Count);
+ }
+
+ [Test]
+ public void Should_Not_Take_In_Account_Inherited_Methods()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.DerivedClass"));
+ Assert.AreEqual(1, blocks.Count);
+ Assert.AreEqual(3, blocks.ElementAt(0).Count);
+ }
+
+ [Test]
+ public void Should_Not_Take_In_Account_Static_Methods()
+ {
+ var analyzer = new Lcom4Analyzer();
+ var blocks = analyzer.FindLcomBlocks(getType("DependencyParser.Test.SimpleClassWithStaticMethod"));
+ Assert.AreEqual(1, blocks.Count);
+ }
+
+ private TypeDefinition getType(string name)
+ {
+ string unit = Assembly.GetExecutingAssembly().Location;
+ var assembly = AssemblyDefinition.ReadAssembly(unit);
+ return assembly.MainModule.GetType(name);
+ }
+
+ }
+
+ public class EmptyClass
+ {
+
+ }
+
+ public interface IEmpty {
+
+ }
+
+ public class SimpleClass
+ {
+ private int counter;
+
+ public void Increment()
+ {
+ counter++;
+ }
+
+ public void Decrement()
+ {
+ counter--;
+ }
+ }
+
+ public class SimpleClassWithCtr {
+ private int fieldA;
+
+ private int fieldB;
+
+ public SimpleClassWithCtr()
+ {
+ fieldA = 42;
+ fieldB = 36;
+ }
+
+ public void doA()
+ {
+ fieldA++;
+ }
+
+ public void doB()
+ {
+ fieldB++;
+ }
+ }
+
+ public class ClassWithManyMethodsAndStrongCohesion {
+
+ public void doA()
+ {
+ Foo();
+ }
+
+ public void doB()
+ {
+ Foo();
+ Bar();
+ }
+
+
+ public void doC()
+ {
+ Bar();
+ }
+
+ private void Foo()
+ {
+ Console.WriteLine("Whatever".ToLower());
+ }
+
+ private void Bar()
+ {
+ Console.WriteLine("Whatever");
+ }
+ }
+
+ public class ClassWithProperties {
+ public int Counter { get; set; }
+
+ public string Name { get; set; }
+ }
+
+ public class ClassWithCallsToAccessors {
+ public int Counter { get; set; }
+
+ public string Name { get; set; }
+
+ public void Foo()
+ {
+ Console.WriteLine("Whatever " + Counter + " " + Name);
+ }
+
+ public void Bar()
+ {
+ Console.WriteLine("Whatever " + Counter + " " + Name);
+ }
+ }
+
+ public class ClassWithComplexProperties {
+ private int counter;
+
+ public int Counter
+ {
+ get
+ {
+ if (counter>10)
+ {
+ return 10;
+ }
+ return counter;
+ }
+
+ set
+ {
+ counter = value;
+ }
+ }
+
+ public void Foo()
+ {
+ Console.WriteLine("Whatever".ToLower());
+ }
+ }
+
+ public abstract class AbstractClass {
+
+ public void DoA()
+ {
+ Foo();
+ }
+
+ public void DoB()
+ {
+ Foo();
+ }
+
+
+ public abstract void DoC();
+
+ private void Foo()
+ {
+ Console.WriteLine("Whatever".ToLower());
+ }
+ }
+
+ public abstract class AbstractTemplateClass {
+
+ public void DoA()
+ {
+ DoC();
+ }
+
+ public void DoB()
+ {
+ DoC();
+ }
+
+ public abstract void DoC();
+
+ }
+
+ public class SimpleDisposableClass : IDisposable
+ {
+ public void DoSomething()
+ {
+ Console.WriteLine("Whatever");
+ }
+
+ public void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public class SimpleClassWithEquals {
+ private int fieldA;
+
+ private int fieldB;
+
+ public void DoA()
+ {
+ fieldA++;
+ }
+
+ public void DoB()
+ {
+ fieldB++;
+ }
+
+ public bool Equals(SimpleClassWithEquals other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return other.fieldA == fieldA && other.fieldB == fieldB;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != typeof (SimpleClassWithEquals)) return false;
+ return Equals((SimpleClassWithEquals) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return (fieldA*397) ^ fieldB;
+ }
+ }
+
+ public static bool operator ==(SimpleClassWithEquals left, SimpleClassWithEquals right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(SimpleClassWithEquals left, SimpleClassWithEquals right)
+ {
+ return !Equals(left, right);
+ }
+ }
+
+ public class SimpleClassWithToString {
+ private int fieldA;
+
+ private int fieldB;
+
+ public void doA()
+ {
+ fieldA++;
+ }
+
+ public void doB()
+ {
+ fieldB++;
+ }
+
+ public override string ToString()
+ {
+ return fieldA + " " + fieldB;
+ }
+ }
+
+ public class SimpleClassWithDelegation {
+ private SimpleClassWithToString wrapped = new SimpleClassWithToString();
+
+ public void doA()
+ {
+ wrapped.doA();
+ }
+
+ public void doB()
+ {
+ wrapped.doB();
+ }
+ }
+
+ public class DerivedClass : SimpleClassWithTwoFields {
+
+ public void doC()
+ {
+ doA();
+ }
+ public void doD()
+ {
+ doA();
+ doA();
+ }
+ }
+
+ public class SimpleClassWithStaticMethod {
+ private int counter;
+
+ public void Increment()
+ {
+ counter++;
+ }
+
+ public void Decrement()
+ {
+ counter--;
+ }
+
+ public static SimpleClassWithStaticMethod create()
+ {
+ Console.WriteLine("factory method");
+ return new SimpleClassWithStaticMethod();
+ }
+ }
+}
View
65 DependencyParser.Test/Lcom4WriterTest.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Xml;
+using Mono.Cecil;
+using NUnit.Framework;
+
+namespace DependencyParser.Test {
+
+ [NUnit.Framework.TestFixture]
+ public class Lcom4WriterTest {
+ [Test]
+ public void Should_Generate()
+ {
+ File.Delete("lcom4-generated.xml");
+ using (var stream = new FileStream("lcom4-generated.xml", FileMode.Create))
+ {
+ using (var writer = new XmlTextWriter(stream, Encoding.UTF8))
+ {
+ writer.Formatting = Formatting.Indented;
+ var lcom4Writer = new Lcom4Writer();
+ var typeDefinition = getType("DependencyParser.Test.SimpleClassWithTwoFields");
+ var blocks = new HashSet<HashSet<MemberReference>>();
+
+ foreach (var mth in typeDefinition.Methods)
+ {
+ var block = new HashSet<MemberReference> { mth, typeDefinition.Fields.First() };
+ blocks.Add(block);
+ }
+ lcom4Writer.Write(writer, typeDefinition, blocks);
+ }
+ }
+ string expected = File.ReadAllText("lcom4-expected.xml");
+ string result = File.ReadAllText("lcom4-generated.xml");
+
+ Assert.AreEqual(expected, result);
+ }
+
+ private TypeDefinition getType(string name)
+ {
+ string unit = Assembly.GetExecutingAssembly().Location;
+ var assembly = AssemblyDefinition.ReadAssembly(unit);
+ return assembly.MainModule.GetType(name);
+ }
+ }
+
+ public class SimpleClassWithTwoFields {
+ private int fieldA;
+
+ private int fieldB;
+
+ public void doA()
+ {
+ fieldA++;
+ }
+
+ public void doB()
+ {
+ fieldB++;
+ }
+ }
+}
View
14 DependencyParser.Test/lcom4-expected.xml
@@ -0,0 +1,14 @@
+<type fullName="DependencyParser.Test.SimpleClassWithTwoFields">
+ <block>
+ <element type="Field" name="fieldA" />
+ <element type="Method" name="System.Void doA()" />
+ </block>
+ <block>
+ <element type="Field" name="fieldA" />
+ <element type="Method" name="System.Void doB()" />
+ </block>
+ <block>
+ <element type="Field" name="fieldA" />
+ <element type="Method" name="System.Void .ctor()" />
+ </block>
+</type>
View
55 DependencyParser.Test/testdata/test.xml
@@ -24,5 +24,60 @@
<From fullname="Example.Core.SampleMeasure/Possible" />
<From fullname="Example.Core.SampleMeasure" />
</TypeReferences>
+ <lcom4>
+ <type fullName="&lt;Module&gt;" />
+ <type fullName="Example.Core.IMoney" />
+ <type fullName="Example.Core.Model.SubType" />
+ <type fullName="Example.Core.Money">
+ <block>
+ <element type="Field" name="fAmount" />
+ <element type="Field" name="fCurrency" />
+ <element type="Method" name="Example.Core.IMoney AddMoney(Example.Core.Money)" />
+ <element type="Method" name="System.Int32 get_Amount()" />
+ <element type="Method" name="System.String get_Currency()" />
+ <element type="Method" name="System.Boolean get_IsZero()" />
+ <element type="Method" name="Example.Core.IMoney Multiply(System.Int32)" />
+ <element type="Method" name="Example.Core.IMoney Negate()" />
+ </block>
+ <block>
+ <element type="Method" name="Example.Core.IMoney AddMoneyBag(Example.Core.MoneyBag)" />
+ </block>
+ <block>
+ <element type="Method" name="Example.Core.IMoney Add(Example.Core.IMoney)" />
+ <element type="Method" name="Example.Core.IMoney Subtract(Example.Core.IMoney)" />
+ </block>
+ </type>
+ <type fullName="Example.Core.Alex" />
+ <type fullName="Example.Core.MoneyBag">
+ <block>
+ <element type="Field" name="fMonies" />
+ <element type="Method" name="Example.Core.IMoney AddMoney(Example.Core.Money)" />
+ <element type="Method" name="Example.Core.IMoney AddMoneyBag(Example.Core.MoneyBag)" />
+ <element type="Method" name="System.Void AppendBag(Example.Core.MoneyBag)" />
+ <element type="Method" name="System.Void AppendMoney(Example.Core.Money)" />
+ <element type="Method" name="System.Boolean Contains(Example.Core.Money)" />
+ <element type="Method" name="Example.Core.Money FindMoney(System.String)" />
+ <element type="Method" name="System.Boolean get_IsZero()" />
+ <element type="Method" name="Example.Core.IMoney Multiply(System.Int32)" />
+ <element type="Method" name="Example.Core.IMoney Negate()" />
+ <element type="Method" name="Example.Core.IMoney Simplify()" />
+ </block>
+ <block>
+ <element type="Method" name="Example.Core.IMoney Add(Example.Core.IMoney)" />
+ <element type="Method" name="Example.Core.IMoney Subtract(Example.Core.IMoney)" />
+ </block>
+ </type>
+ <type fullName="Example.Core.SampleMeasure">
+ <block>
+ <element type="Field" name="Size (property)" />
+ <element type="Field" name="actual" />
+ <element type="Method" name="System.String Compute()" />
+ <element type="Method" name="System.String Compute(System.String)" />
+ </block>
+ <block>
+ <element type="Method" name="System.String Compute(System.Int32)" />
+ </block>
+ </type>
+ </lcom4>
</Assembly>
</Dependencies>
View
14 DependencyParser.sln
@@ -4,12 +4,16 @@ Microsoft Visual Studio Solution File, Format Version 11.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyParser", "DependencyParser\DependencyParser.csproj", "{AE47EF64-9F61-463A-9088-79907968A1FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyParser.Test", "DependencyParser.Test\DependencyParser.Test.csproj", "{F2D48840-011D-40D3-8CE9-6796A2FB237F}"
+ ProjectSection(ProjectDependencies) = postProject
+ {AE47EF64-9F61-463A-9088-79907968A1FE} = {AE47EF64-9F61-463A-9088-79907968A1FE}
+ EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{9035F906-9992-4C15-9274-756884AC2259}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.Config = .nuget\NuGet.Config
.nuget\NuGet.exe = .nuget\NuGet.exe
.nuget\NuGet.targets = .nuget\NuGet.targets
+ .nuget\packages.config = .nuget\packages.config
EndProjectSection
EndProject
Global
@@ -23,11 +27,13 @@ Global
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AE47EF64-9F61-463A-9088-79907968A1FE}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {AE47EF64-9F61-463A-9088-79907968A1FE}.Debug|Any CPU.Build.0 = Debug|x86
{AE47EF64-9F61-463A-9088-79907968A1FE}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{AE47EF64-9F61-463A-9088-79907968A1FE}.Debug|Mixed Platforms.Build.0 = Debug|x86
- {AE47EF64-9F61-463A-9088-79907968A1FE}.Debug|x86.ActiveCfg = Debug|x86
- {AE47EF64-9F61-463A-9088-79907968A1FE}.Debug|x86.Build.0 = Debug|x86
+ {AE47EF64-9F61-463A-9088-79907968A1FE}.Debug|x86.ActiveCfg = Release|x86
+ {AE47EF64-9F61-463A-9088-79907968A1FE}.Debug|x86.Build.0 = Release|x86
{AE47EF64-9F61-463A-9088-79907968A1FE}.Release|Any CPU.ActiveCfg = Release|x86
+ {AE47EF64-9F61-463A-9088-79907968A1FE}.Release|Any CPU.Build.0 = Release|x86
{AE47EF64-9F61-463A-9088-79907968A1FE}.Release|Mixed Platforms.ActiveCfg = Release|x86
{AE47EF64-9F61-463A-9088-79907968A1FE}.Release|Mixed Platforms.Build.0 = Release|x86
{AE47EF64-9F61-463A-9088-79907968A1FE}.Release|x86.ActiveCfg = Release|x86
@@ -36,12 +42,14 @@ Global
{F2D48840-011D-40D3-8CE9-6796A2FB237F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2D48840-011D-40D3-8CE9-6796A2FB237F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F2D48840-011D-40D3-8CE9-6796A2FB237F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {F2D48840-011D-40D3-8CE9-6796A2FB237F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F2D48840-011D-40D3-8CE9-6796A2FB237F}.Debug|x86.ActiveCfg = Release|Any CPU
+ {F2D48840-011D-40D3-8CE9-6796A2FB237F}.Debug|x86.Build.0 = Release|Any CPU
{F2D48840-011D-40D3-8CE9-6796A2FB237F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2D48840-011D-40D3-8CE9-6796A2FB237F}.Release|Any CPU.Build.0 = Release|Any CPU
{F2D48840-011D-40D3-8CE9-6796A2FB237F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F2D48840-011D-40D3-8CE9-6796A2FB237F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F2D48840-011D-40D3-8CE9-6796A2FB237F}.Release|x86.ActiveCfg = Release|Any CPU
+ {F2D48840-011D-40D3-8CE9-6796A2FB237F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
View
14 DependencyParser/DependencyParser.csproj
@@ -10,8 +10,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DependencyParser</RootNamespace>
<AssemblyName>DependencyParser</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
- <TargetFrameworkProfile>Client</TargetFrameworkProfile>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <TargetFrameworkProfile>
+ </TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
@@ -66,6 +67,9 @@
<Reference Include="NDesk.Options, Version=0.2.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NDesk.Options.0.2.1\lib\NDesk.Options.dll</HintPath>
</Reference>
+ <Reference Include="Gendarme.Framework">
+ <HintPath>..\packages\Mono.Gendarme.2.11.0.20121120\tools\Gendarme.Framework.dll</HintPath>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@@ -74,12 +78,16 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Lcom4Analyzer.cs" />
+ <Compile Include="Lcom4Writer.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
- <None Include="packages.config" />
+ <None Include="packages.config">
+ <SubType>Designer</SubType>
+ </None>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
View
156 DependencyParser/Lcom4Analyzer.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Gendarme.Framework.Rocks;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+namespace DependencyParser {
+
+
+ /// <summary>
+ /// LCOM4 analyzer.
+ /// See following page for detailed explanations on LCOM4:
+ /// http://www.aivosto.com/project/help/pm-oo-cohesion.html#LCOM4
+ /// </summary>
+ public class Lcom4Analyzer {
+
+ public HashSet<HashSet<MemberReference>> FindLcomBlocks(TypeDefinition t)
+ {
+ var memberBlocks = new Dictionary<MemberReference, HashSet<MemberReference>>();
+ foreach (var method in t.Methods)
+ {
+ if (NeedToBeFiltered(t, method))
+ {
+ continue;
+ }
+
+ HashSet<MemberReference> currentBlock = null;
+ if (memberBlocks.ContainsKey(method))
+ {
+ currentBlock = memberBlocks[method];
+ } else
+ {
+ currentBlock = new HashSet<MemberReference>();
+ currentBlock.Add(method);
+ memberBlocks[method] = currentBlock;
+ }
+
+ if (method.HasBody)
+ {
+ foreach (Instruction inst in method.Body.Instructions)
+ {
+ MemberReference mr = null;
+
+ switch (inst.OpCode.OperandType)
+ {
+ case OperandType.InlineField:
+ FieldDefinition fd = inst.Operand as FieldDefinition;
+ if (null != fd && (!fd.IsGeneratedCode() || method.IsSetter || method.IsGetter))
+ {
+ mr = fd;
+ }
+ break;
+ case OperandType.InlineMethod:
+ // special case for automatic properties since the 'backing' fields won't be used
+ MethodDefinition md = inst.Operand as MethodDefinition;
+ if (md != null && !NeedToBeFiltered(t, md))
+ {
+ mr = md;
+ }
+ break;
+ }
+ if (mr != null && !currentBlock.Contains(mr))
+ {
+
+ if (memberBlocks.ContainsKey(mr))
+ {
+ memberBlocks[mr].UnionWith(currentBlock);
+ currentBlock = memberBlocks[mr];
+ }
+ else
+ {
+ currentBlock.Add(mr);
+ memberBlocks[mr] = currentBlock;
+ }
+ }
+ }
+ }
+ memberBlocks[method] = currentBlock;
+
+ }
+ return CleanBlocks(MergeBlocks(memberBlocks.Values));
+ }
+
+ private bool NeedToBeFiltered(TypeDefinition t, MethodDefinition method)
+ {
+ return (!t.AllSuperTypes().Contains(method.DeclaringType)
+ || method.IsStatic
+ || (method.IsGeneratedCode() && !method.IsSetter && !method.IsGetter)
+ || method.IsConstructor
+ || (method.IsSpecialName && !method.IsSetter && !method.IsGetter)
+ || "Dispose" == method.Name
+ || "ToString" == method.Name
+ || "Equals" == method.Name
+ || "GetHashCode" == method.Name);
+
+ }
+
+ private HashSet<HashSet<MemberReference>> MergeBlocks(IEnumerable<HashSet<MemberReference>> memberBlocks)
+ {
+ var inputBlocks = new HashSet<HashSet<MemberReference>>(memberBlocks);
+ var blocks = new HashSet<HashSet<MemberReference>>();
+ while (inputBlocks.Count > 0)
+ {
+ var block = inputBlocks.First();
+ inputBlocks.Remove(block);
+ var blocksToMerge = new HashSet<HashSet<MemberReference>>();
+ foreach (var mergeCandidate in inputBlocks)
+ {
+ if (CanBeMerged(block, mergeCandidate))
+ {
+ blocksToMerge.Add(mergeCandidate);
+ }
+ }
+ if (blocksToMerge.Count==0)
+ {
+ blocks.Add(block);
+ }
+ else
+ {
+ foreach (var blockToMerge in blocksToMerge)
+ {
+ inputBlocks.Remove(blockToMerge);
+ block.UnionWith(blockToMerge);
+ }
+ inputBlocks.Add(block);
+ }
+ }
+ return blocks;
+ }
+
+ private bool CanBeMerged(HashSet<MemberReference> block, HashSet<MemberReference> mergeCandidate)
+ {
+ var intersection = block.Intersect(mergeCandidate);
+ return intersection.Count() > 0;
+ }
+
+ private HashSet<HashSet<MemberReference>> CleanBlocks(HashSet<HashSet<MemberReference>> blocks)
+ {
+ foreach (var block in blocks)
+ {
+ block.RemoveWhere(ShouldBeRemoved);
+ }
+
+ blocks.RemoveWhere(b => b.Count(member => member is MethodDefinition) == 0);
+ return blocks;
+ }
+
+ private bool ShouldBeRemoved(MemberReference reference)
+ {
+ var method = reference as MethodDefinition;
+ return method!=null && (!method.HasBody || method.IsGeneratedCode());
+ }
+ }
+}
View
100 DependencyParser/Lcom4Writer.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using Mono.Cecil;
+
+namespace DependencyParser {
+
+
+ /// <summary>
+ /// TODO: Update summary.
+ /// </summary>
+ public class Lcom4Writer {
+
+ private readonly ReferenceComparer comparer = new ReferenceComparer();
+
+ public void Write(XmlTextWriter xml, TypeDefinition type, IEnumerable<IEnumerable<MemberReference>> blocks)
+ {
+ xml.WriteStartElement("type");
+ xml.WriteAttributeString("fullName", type.FullName);
+ foreach (var block in blocks)
+ {
+ var orderedBlock = block.OrderBy(x => x, comparer);
+
+ xml.WriteStartElement("block");
+ foreach (var memberReference in orderedBlock)
+ {
+ xml.WriteStartElement("element");
+ var method = memberReference as MethodDefinition;
+ string name;
+ string elementType;
+ if (method == null)
+ {
+ elementType = "Field";
+ name = BuildFieldName(memberReference as FieldDefinition);
+ } else
+ {
+ elementType = "Method";
+ name = BuildSignature(method);
+ }
+ xml.WriteAttributeString("type", elementType);
+ xml.WriteAttributeString("name", name);
+ xml.WriteEndElement();
+ }
+ xml.WriteEndElement();
+ }
+ xml.WriteEndElement();
+ }
+
+ private string BuildFieldName(FieldDefinition field)
+ {
+ string name;
+ if (field.Name.Contains("BackingField"))
+ {
+ name = field.Name.Split(new string[] { "<", ">" }, StringSplitOptions.RemoveEmptyEntries)[0] + " (property)";
+ } else
+ {
+ name = field.Name;
+ }
+ return name;
+ }
+
+ private string BuildSignature(MethodDefinition mth)
+ {
+ var builder = new StringBuilder();
+ builder.Append(mth.ReturnType);
+ builder.Append(" ");
+ builder.Append(mth.Name);
+ builder.Append("(");
+ var parameters = mth.Parameters;
+ for (int i = 0; i < parameters.Count; i++)
+ {
+ builder.Append(parameters[i].ParameterType.FullName);
+ if (i < (parameters.Count-1))
+ {
+ builder.Append(", ");
+ }
+ }
+ builder.Append(")");
+ return builder.ToString();
+ }
+
+ private class ReferenceComparer : IComparer<MemberReference>
+ {
+ public int Compare(MemberReference ref1, MemberReference ref2)
+ {
+ if (ref1 is MethodDefinition && ref2 is FieldDefinition)
+ {
+ return 1;
+ }
+ if (ref2 is MethodDefinition && ref1 is FieldDefinition) {
+ return -1;
+ }
+
+ return string.Compare(ref1.Name, ref2.Name);
+ }
+ }
+ }
+}
View
17 DependencyParser/Program.cs
@@ -15,7 +15,11 @@ public class Program
private static readonly List<string> Parsed = new List<string>();
private static readonly List<string> ToParse = new List<string>();
-
+
+ private static readonly Lcom4Analyzer lcom4Analyzer = new Lcom4Analyzer();
+
+ private static readonly Lcom4Writer lcom4Writer = new Lcom4Writer();
+
public static void Main(string[] args)
{
bool showHelp = false;
@@ -162,6 +166,11 @@ static void Analysis(XmlTextWriter writer, ModuleDefinition module, string fullP
}
writer.WriteEndElement();
+ writer.WriteStartElement("lcom4");
+ foreach (var t in module.Types) {
+ ParseTypeLcom4Blocks(writer, t);
+ }
+ writer.WriteEndElement();
}
writer.WriteEndElement();
@@ -174,6 +183,12 @@ static void Analysis(XmlTextWriter writer, ModuleDefinition module, string fullP
Parsed.Add(module.Assembly.Name.FullName);
}
+ public static void ParseTypeLcom4Blocks(XmlTextWriter writer, TypeDefinition t)
+ {
+ var blocks = lcom4Analyzer.FindLcomBlocks(t);
+ lcom4Writer.Write(writer, t, blocks);
+ }
+
public static void ParseType(XmlTextWriter writer, TypeDefinition t)
{
// ignore generated types
View
5 DependencyParser/app.config
@@ -1,3 +1,6 @@
<?xml version="1.0"?>
<configuration>
-<startup><supportedRuntime version="v2.0.50727" sku="Client"/></startup></configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
+ </startup>
+</configuration>
View
1 DependencyParser/packages.config
@@ -2,4 +2,5 @@
<packages>
<package id="Mono.Cecil" version="0.9.5.4" targetFramework="net35-Client" />
<package id="NDesk.Options" version="0.2.1" targetFramework="net35-Client" />
+ <package id="Mono.Gendarme" version="2.11.0.20121120" targetFramework="net35-Client" />
</packages>
View
1 pom.xml
@@ -9,6 +9,7 @@
<properties>
<visual.studio.solution>DependencyParser.sln</visual.studio.solution>
<dotnet.tool.version>4.0</dotnet.tool.version>
+ <visual.test.project.pattern>*.Tests;*Test</visual.test.project.pattern>
</properties>
<build>
<plugins>

0 comments on commit 74933f3

Please sign in to comment.