├── .gitignore ├── ArchitectureScript.sln ├── ArchitectureScript ├── App.config ├── ArchitectureScript.csproj ├── Model │ ├── Component.cs │ ├── ComponentView.cs │ ├── Container.cs │ ├── ContainerView.cs │ ├── ContextView.cs │ ├── Element.cs │ ├── ElementView.cs │ ├── Model.cs │ ├── Person.cs │ ├── Relationship.cs │ ├── RelationshipView.cs │ ├── SoftwareSystem.cs │ └── View.cs ├── Parsing │ ├── ComponentSegmentWalker.cs │ ├── ContainerSegmentWalker.cs │ ├── ElementSegmentWalker.cs │ ├── ISegmentWalker.cs │ ├── ModelSegmentWalker.cs │ ├── ParseAttribute.cs │ ├── Parser.cs │ ├── PersonSegmentWalker.cs │ ├── RelationshipSegmentWalker.cs │ ├── Segment.cs │ ├── SegmentWalkerBase.cs │ ├── SystemSegmentWalker.cs │ └── ViewSegmentWalker.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | #OS junk files 2 | [Tt]humbs.db 3 | *.DS_Store 4 | 5 | #Visual Studio files 6 | *.[Oo]bj 7 | *.user 8 | *.aps 9 | *.pch 10 | *.vspscc 11 | *.vssscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.[Cc]ache 20 | *.ilk 21 | *.log 22 | *.lib 23 | *.sbr 24 | *.sdf 25 | *.opensdf 26 | *.unsuccessfulbuild 27 | ipch/ 28 | [Oo]bj/ 29 | [Bb]in 30 | [Dd]ebug*/ 31 | [Rr]elease*/ 32 | Ankh.NoLoad 33 | 34 | #MonoDevelop 35 | *.pidb 36 | *.userprefs 37 | 38 | #Tooling 39 | _ReSharper*/ 40 | *.resharper 41 | [Tt]est[Rr]esult* 42 | *.sass-cache 43 | 44 | #Project files 45 | [Bb]uild/ 46 | 47 | #Subversion files 48 | .svn 49 | 50 | # Office Temp Files 51 | ~$* 52 | 53 | # vim Temp Files 54 | *~ 55 | 56 | #NuGet 57 | packages/ 58 | *.nupkg 59 | 60 | #ncrunch 61 | *ncrunch* 62 | *crunch*.local.xml 63 | 64 | # visual studio database projects 65 | *.dbmdl 66 | 67 | #Test files 68 | *.testsettings -------------------------------------------------------------------------------- /ArchitectureScript.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchitectureScript", "ArchitectureScript\ArchitectureScript.csproj", "{07EDBD84-B267-4995-A8F6-D7B8D6BF100D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {07EDBD84-B267-4995-A8F6-D7B8D6BF100D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {07EDBD84-B267-4995-A8F6-D7B8D6BF100D}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {07EDBD84-B267-4995-A8F6-D7B8D6BF100D}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {07EDBD84-B267-4995-A8F6-D7B8D6BF100D}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /ArchitectureScript/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ArchitectureScript/ArchitectureScript.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {07EDBD84-B267-4995-A8F6-D7B8D6BF100D} 8 | Exe 9 | Properties 10 | ArchitectureScript 11 | ArchitectureScript 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | False 37 | ..\packages\Newtonsoft.Json.6.0.3\lib\net45\Newtonsoft.Json.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 89 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/Component.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class Component : Element 10 | { 11 | public string Type { get { return "Component"; } } 12 | public string Technology { get; set; } 13 | public string ImplementationType { get; set; } 14 | public string InterfaceType { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/ComponentView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class ComponentView : View 10 | { 11 | private readonly Container _container; 12 | 13 | public ComponentView(Container container) : base(container.Parent) 14 | { 15 | _container = container; 16 | Name = String.Format("{0} - {1} - Components", container.Parent.Name, _container.Name); 17 | } 18 | 19 | 20 | public int ContainerId { get { return _container.Id; } } 21 | public string Type { get { return "Component"; } } 22 | 23 | public override void Normalize() 24 | { 25 | foreach (var system in Model.SoftwareSystems.Where(x => x.Id != SoftwareSystemId)) 26 | AddElement(system); 27 | 28 | foreach (var container in System.Containers.Where(x => x.Id != _container.Id)) 29 | AddElement(container); 30 | 31 | foreach(var component in _container.Components) 32 | AddElement(component); 33 | 34 | base.Normalize(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/Container.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class Container : Element 10 | { 11 | private readonly IList _components = new List(); 12 | 13 | public string Technology { get; set; } 14 | public string Type { get { return "Container"; } } 15 | public IEnumerable Components { get { return _components; } } 16 | 17 | internal SoftwareSystem Parent { get; set; } 18 | 19 | public Component AddComponent(string name) 20 | { 21 | var component = Model.AddComponent(name); 22 | AddComponent(component); 23 | return component; 24 | } 25 | 26 | public void AddComponent(Component component) 27 | { 28 | _components.Add(component); 29 | component.Location = this.Location; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/ContainerView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class ContainerView : View 10 | { 11 | public ContainerView(SoftwareSystem system) : base(system) 12 | { 13 | Name = String.Format("{0} - Containers", system.Name); 14 | } 15 | 16 | public string Type { get { return "Container"; } } 17 | 18 | public override void Normalize() 19 | { 20 | foreach (var system in Model.SoftwareSystems.Where(x => x.Id != SoftwareSystemId)) 21 | AddElement(system); 22 | 23 | foreach (var person in Model.People) 24 | AddElement(person); 25 | 26 | foreach (var container in System.Containers) 27 | AddElement(container); 28 | 29 | base.Normalize(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/ContextView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class ContextView : View 10 | { 11 | public ContextView(SoftwareSystem system) : base(system) { } 12 | 13 | public string Type { get { return "SystemContext"; } } 14 | 15 | public override void Normalize() 16 | { 17 | foreach (var system in Model.SoftwareSystems) 18 | AddElement(system); 19 | 20 | foreach (var person in Model.People) 21 | AddElement(person); 22 | 23 | base.Normalize(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/Element.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public abstract class Element 10 | { 11 | public Model Model { get; set; } 12 | public int Id { get; set; } 13 | public string Name { get; set; } 14 | public string Description { get; set; } 15 | public string Location { get; set; } 16 | 17 | private readonly IList _relationships = new List(); 18 | 19 | public Relationship AddRelationship(string destination, string description) 20 | { 21 | var relationship = new Relationship(); 22 | relationship.Source = this; 23 | relationship.DestinationTag = destination; 24 | relationship.Description = description; 25 | _relationships.Add(relationship); 26 | return relationship; 27 | } 28 | 29 | public void Normalise() 30 | { 31 | foreach (var relationship in _relationships) 32 | { 33 | relationship.Destination = Model.GetElementByName(relationship.DestinationTag); 34 | } 35 | } 36 | 37 | public IEnumerable Relationships 38 | { 39 | get { return _relationships; } 40 | } 41 | 42 | public bool ShouldSerializeModel() 43 | { 44 | return false; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/ElementView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class ElementView 10 | { 11 | public int Id { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/Model.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class Model 10 | { 11 | private readonly IList _elements = new List(); 12 | private readonly IList _views = new List(); 13 | private readonly object _idLock = new object(); 14 | 15 | public IEnumerable SoftwareSystems { get { return _elements.OfType().ToList(); } } 16 | public IEnumerable People { get { return _elements.OfType().ToList(); } } 17 | public IEnumerable SystemContextViews { get { return _views.OfType().ToList(); } } 18 | public IEnumerable ContainerViews { get { return _views.OfType().ToList(); } } 19 | public IEnumerable ComponentViews { get { return _views.OfType().ToList(); } } 20 | 21 | public Element GetElementByName(string name) 22 | { 23 | return _elements.SingleOrDefault(x => x.Name == name); 24 | } 25 | 26 | public void Normalise() 27 | { 28 | foreach (var element in _elements) 29 | { 30 | element.Normalise(); 31 | } 32 | 33 | foreach (var view in _views) 34 | { 35 | view.Normalize(); 36 | } 37 | } 38 | 39 | public SoftwareSystem AddSoftwareSystem(string location, string name) 40 | { 41 | var system = new SoftwareSystem 42 | { 43 | Id = GetId(), 44 | Location = location, 45 | Name = name, 46 | Model = this 47 | }; 48 | 49 | _elements.Add(system); 50 | 51 | return system; 52 | } 53 | 54 | private int GetId() 55 | { 56 | lock (_idLock) 57 | return _elements.Select(x => x.Id).Concat(new[] { 0 }).Max() + 1; 58 | } 59 | 60 | public Person AddPerson(string location, string name) 61 | { 62 | var person = new Person 63 | { 64 | Id = GetId(), 65 | Location = location, 66 | Name = name, 67 | Model = this 68 | }; 69 | 70 | _elements.Add(person); 71 | 72 | return person; 73 | } 74 | 75 | public Container AddContainer(string name) 76 | { 77 | var container = new Container 78 | { 79 | Id = GetId(), 80 | Name = name, 81 | Model = this 82 | }; 83 | 84 | _elements.Add(container); 85 | 86 | return container; 87 | } 88 | 89 | public Component AddComponent(string name) 90 | { 91 | var component = new Component 92 | { 93 | Id = GetId(), 94 | Name = name, 95 | Model = this 96 | }; 97 | 98 | _elements.Add(component); 99 | 100 | return component; 101 | } 102 | 103 | public ContextView AddContextView(string system) 104 | { 105 | var softwareSystem = GetElementByName(system) as SoftwareSystem; 106 | var contextView = new ContextView(softwareSystem); 107 | contextView.Model = this; 108 | _views.Add(contextView); 109 | return contextView; 110 | } 111 | 112 | public ContainerView AddContainerView(string system) 113 | { 114 | var softwareSystem = GetElementByName(system) as SoftwareSystem; 115 | var containerView = new ContainerView(softwareSystem); 116 | containerView.Model = this; 117 | _views.Add(containerView); 118 | return containerView; 119 | } 120 | 121 | public ComponentView AddComponentView(string tag) 122 | { 123 | var container = GetElementByName(tag) as Container; 124 | var componentView = new ComponentView(container); 125 | componentView.Model = this; 126 | _views.Add(componentView); 127 | return componentView; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class Person : Element 10 | { 11 | public string Type { get { return "Person"; } } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/Relationship.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class Relationship 10 | { 11 | public Element Source { get; set; } 12 | public Element Destination { get; set; } 13 | public string Description { get; set; } 14 | public string DestinationTag { get; set; } 15 | 16 | public bool ShouldSerializeSource() { return false; } 17 | public bool ShouldSerializeDestination() { return false; } 18 | public bool ShouldSerializeDestinationTag() { return false; } 19 | 20 | public int SourceId { get { return Source.Id; } } 21 | public int DestinationId { get { return Destination.Id; } } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/RelationshipView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class RelationshipView 10 | { 11 | public int SourceId { get; set; } 12 | public int DestinationId { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/SoftwareSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public class SoftwareSystem : Element 10 | { 11 | private IList _containers = new List(); 12 | 13 | public string Type { get { return "SoftwareSystem"; } } 14 | 15 | public Container AddContainer(string name, string description, string technology) 16 | { 17 | var container = Model.AddContainer(name); 18 | AddContainer(container); 19 | return container; 20 | } 21 | 22 | public void AddContainer(Container container) 23 | { 24 | container.Location = this.Location; 25 | container.Parent = this; 26 | _containers.Add(container); 27 | } 28 | 29 | 30 | public IEnumerable Containers { get { return _containers; } } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ArchitectureScript/Model/View.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Model 8 | { 9 | public abstract class View 10 | { 11 | private readonly SoftwareSystem _system; 12 | protected IList _elements = new List(); 13 | private readonly IList _exclusions = new List(); 14 | 15 | public View(SoftwareSystem system) 16 | { 17 | _system = system; 18 | Description = ""; 19 | } 20 | 21 | public string Name { get; set; } 22 | public string Description { get; set; } 23 | public Model Model { get; set; } 24 | public IEnumerable Elements { get { return _elements.Select(x => new ElementView { Id = x.Id }); } } 25 | public int SoftwareSystemId { get { return _system.Id; } } 26 | protected SoftwareSystem System { get { return _system; } } 27 | 28 | 29 | 30 | public IEnumerable Relationships 31 | { 32 | get 33 | { 34 | var elementIds = _elements.Select(x => x.Id).ToArray(); 35 | return from e in _elements 36 | from r in e.Relationships 37 | where elementIds.Contains(r.SourceId) && elementIds.Contains(r.DestinationId) 38 | select new RelationshipView { SourceId = r.SourceId, DestinationId = r.DestinationId }; 39 | } 40 | } 41 | 42 | public void AddElement(Element element) 43 | { 44 | if (!_exclusions.Contains(element.Id)) 45 | _elements.Add(element); 46 | } 47 | 48 | 49 | public virtual void Normalize() 50 | { 51 | var reachableIds = Relationships.SelectMany(x => new[] { x.SourceId, x.DestinationId }).ToArray(); 52 | _elements = _elements.Where(x => reachableIds.Contains(x.Id) && !_exclusions.Contains(x.Id)).ToList(); 53 | } 54 | 55 | public bool ShouldSerializeModel() 56 | { 57 | return false; 58 | } 59 | 60 | public void AddExclusion(string tag) 61 | { 62 | _exclusions.Add(Model.GetElementByName(tag).Id); 63 | } 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/ComponentSegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using ArchitectureScript.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | public class ComponentSegmentWalker : ElementSegmentWalker 11 | { 12 | private readonly Component _component; 13 | 14 | public ComponentSegmentWalker(Component component) : base(component) 15 | { 16 | _component = component; 17 | } 18 | 19 | [Parse("tech (.*)")] 20 | public void SetTechnology(string technology) 21 | { 22 | _component.Technology = technology; 23 | } 24 | 25 | [Parse("implementation (.*)")] 26 | public void SetImplementationType(string implementationType) 27 | { 28 | _component.ImplementationType = implementationType; 29 | } 30 | 31 | [Parse("interface (.*)")] 32 | public void SetInterfaceType(string interfaceType) 33 | { 34 | _component.InterfaceType = interfaceType; 35 | } 36 | 37 | [Parse("container (.*)")] 38 | public void SetContainer(string tag) 39 | { 40 | var container = _component.Model.GetElementByName(tag) as Container; 41 | 42 | container.AddComponent(_component); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/ContainerSegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using ArchitectureScript.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | public class ContainerSegmentWalker : ElementSegmentWalker 11 | { 12 | private readonly Container _container; 13 | 14 | public ContainerSegmentWalker(Container container) : base(container) 15 | { 16 | _container = container; 17 | } 18 | 19 | [Parse("tech (.*)")] 20 | public void SetTechnology(string technology) 21 | { 22 | _container.Technology = technology; 23 | } 24 | 25 | [Parse("component (.*)")] 26 | public ComponentSegmentWalker AddComponent(string tag) 27 | { 28 | var component = _container.AddComponent(tag); 29 | return new ComponentSegmentWalker(component); 30 | } 31 | 32 | [Parse("system (.*)")] 33 | public void SetSystem(string tag) 34 | { 35 | var system = _container.Model.GetElementByName(tag) as SoftwareSystem; 36 | system.AddContainer(_container); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/ElementSegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using ArchitectureScript.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | public class ElementSegmentWalker : SegmentWalkerBase where T : SegmentWalkerBase 11 | { 12 | private readonly Element _element; 13 | 14 | public ElementSegmentWalker (Element element) 15 | { 16 | _element = element; 17 | } 18 | 19 | [Parse("uses (.*)")] 20 | public RelationshipSegmentWalker AddUse(string tag) 21 | { 22 | var relationship = _element.AddRelationship(tag, null); 23 | return new RelationshipSegmentWalker(relationship); 24 | } 25 | 26 | [Parse("user (.*)")] 27 | public RelationshipSegmentWalker AddUser(string tag) 28 | { 29 | var user = _element.Model.GetElementByName(tag); 30 | var relationship = user.AddRelationship(_element.Name, null); 31 | return new RelationshipSegmentWalker(relationship); 32 | } 33 | 34 | [Parse("desc (.*)")] 35 | public void SetDescription(string description) 36 | { 37 | _element.Description = description; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/ISegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Parsing 8 | { 9 | public interface ISegmentWalker 10 | { 11 | void Walk(IEnumerable segments); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/ModelSegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | class ModelSegmentWalker : SegmentWalkerBase 11 | { 12 | private readonly ArchitectureScript.Model.Model _model; 13 | 14 | public ModelSegmentWalker(ArchitectureScript.Model.Model model) 15 | { 16 | _model = model; 17 | } 18 | 19 | [Parse("internal system (.*)")] 20 | [Parse("system (.*)")] 21 | public SystemSegmentWalker AddInternalSystem(string tag) 22 | { 23 | var system = _model.AddSoftwareSystem("Internal", tag); 24 | return new SystemSegmentWalker(system); 25 | } 26 | 27 | [Parse("external system (.*)")] 28 | public SystemSegmentWalker AddExternalSystem(string tag) 29 | { 30 | var system = _model.AddSoftwareSystem("External", tag); 31 | return new SystemSegmentWalker(system); 32 | } 33 | 34 | [Parse("person (.*)")] 35 | [Parse("internal person (.*)")] 36 | public PersonSegmentWalker AddInternalPerson(string tag) 37 | { 38 | var person = _model.AddPerson("Internal", tag); 39 | return new PersonSegmentWalker(person); 40 | } 41 | 42 | [Parse("external person (.*)")] 43 | public PersonSegmentWalker AddExternalPerson(string tag) 44 | { 45 | var person = _model.AddPerson("External", tag); 46 | return new PersonSegmentWalker(person); 47 | } 48 | 49 | [Parse("container (.*)")] 50 | public ContainerSegmentWalker AddContainer(string tag) 51 | { 52 | var container = _model.AddContainer(tag); 53 | return new ContainerSegmentWalker(container); 54 | } 55 | 56 | [Parse("component (.*)")] 57 | public ComponentSegmentWalker AddComponent(string tag) 58 | { 59 | var component = _model.AddComponent(tag); 60 | return new ComponentSegmentWalker(component); 61 | } 62 | 63 | [Parse("view context (.*)")] 64 | public ViewSegmentWalker AddContextView(string system) 65 | { 66 | var view = _model.AddContextView(system); 67 | return new ViewSegmentWalker(view); 68 | } 69 | 70 | [Parse("view containers (.*)")] 71 | public ViewSegmentWalker AddContainersView(string system) 72 | { 73 | var view = _model.AddContainerView(system); 74 | return new ViewSegmentWalker(view); 75 | } 76 | 77 | [Parse("view components (.*)")] 78 | public ViewSegmentWalker AddComponentsView(string container) 79 | { 80 | var view = _model.AddComponentView(container); 81 | return new ViewSegmentWalker(view); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/ParseAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | [AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited= true)] 11 | public class ParseAttribute : Attribute 12 | { 13 | private readonly string _text; 14 | 15 | public ParseAttribute(string text) 16 | { 17 | _text = text; 18 | } 19 | 20 | public Regex CreateRegex() 21 | { 22 | var text = "^" + _text + "$"; 23 | var options = RegexOptions.Compiled | RegexOptions.IgnoreCase; 24 | return new Regex(text, options); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/Parser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ArchitectureScript.Parsing 8 | { 9 | public class Parser 10 | { 11 | public Model.Model CreateModel(string script) 12 | { 13 | var model = new Model.Model(); 14 | UpdateModel(model, script); 15 | return model; 16 | } 17 | 18 | public void UpdateModel(Model.Model model, string script) 19 | { 20 | var segments = Segment.Parse(script); 21 | var walker = new ModelSegmentWalker(model) as ISegmentWalker; 22 | walker.Walk(segments); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/PersonSegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using ArchitectureScript.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | public class PersonSegmentWalker : ElementSegmentWalker 11 | { 12 | public PersonSegmentWalker(Person person) : base(person) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/RelationshipSegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using ArchitectureScript.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | public class RelationshipSegmentWalker : SegmentWalkerBase 11 | { 12 | private readonly Relationship _relationship; 13 | 14 | public RelationshipSegmentWalker(Relationship relationship) 15 | { 16 | _relationship = relationship; 17 | } 18 | 19 | [Parse("(.*)")] 20 | public void AddComment(string comment) 21 | { 22 | if (String.IsNullOrWhiteSpace(_relationship.Description)) 23 | _relationship.Description = comment; 24 | else 25 | _relationship.Description += Environment.NewLine + comment; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/Segment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | public class Segment 11 | { 12 | private readonly IList _segments; 13 | 14 | private Segment(string text) 15 | { 16 | Text = text; 17 | _segments = new List(); 18 | } 19 | 20 | public string Text { get; private set; } 21 | public IEnumerable SubSegments { get { return _segments.AsEnumerable(); } } 22 | 23 | public void Add(Segment s) 24 | { 25 | _segments.Add(s); 26 | } 27 | 28 | public static IEnumerable Parse(string s) 29 | { 30 | var segments = new List(); 31 | 32 | var stack = new Stack>(); 33 | 34 | var lines = s.Split('\n'); 35 | 36 | foreach (var line in lines) 37 | { 38 | var match = Regex.Match(line, @"^(\s*)(.*)$"); 39 | var whiteSpace = match.Result("$1").Replace("\t", " "); 40 | var text = match.Result("$2"); 41 | 42 | if (String.IsNullOrWhiteSpace(text)) 43 | { 44 | stack.Clear(); 45 | continue; 46 | } 47 | 48 | var segment = new Segment(text.Trim()); 49 | 50 | var indent = whiteSpace.Length; 51 | 52 | if (stack.Any()) 53 | { 54 | while (stack.Any() && stack.Peek().Item1 >= indent) 55 | stack.Pop(); 56 | if (stack.Any()) 57 | stack.Peek().Item2.Add(segment); 58 | } 59 | 60 | if (stack.Any() == false) 61 | segments.Add(segment); 62 | stack.Push(Tuple.Create(indent, segment)); 63 | } 64 | 65 | return segments; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/SegmentWalkerBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | 9 | namespace ArchitectureScript.Parsing 10 | { 11 | public abstract class SegmentWalkerBase : ISegmentWalker where T : SegmentWalkerBase 12 | { 13 | private static readonly IEnumerable> _parsers; 14 | 15 | static SegmentWalkerBase () 16 | { 17 | _parsers = (from method in typeof(T).GetMethods() 18 | from parse in method.GetCustomAttributes(typeof(ParseAttribute), true).OfType() 19 | select Tuple.Create(parse.CreateRegex(), method)).ToList(); 20 | 21 | } 22 | 23 | void ISegmentWalker.Walk(IEnumerable segments) 24 | { 25 | foreach(var segment in segments) 26 | { 27 | var executionContext = (from p in _parsers 28 | let match = p.Item1.Match(segment.Text) 29 | where match.Success 30 | select new { match, method = p.Item2 }).FirstOrDefault(); 31 | 32 | 33 | if (executionContext != null) 34 | { 35 | var parameters = executionContext.method.GetParameters().Select((p, i) => executionContext.match.Groups[i + 1].Value).Cast().ToArray(); 36 | var subwalker = executionContext.method.Invoke(this, parameters) as ISegmentWalker; 37 | if (subwalker != null) 38 | { 39 | subwalker.Walk(segment.SubSegments); 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/SystemSegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using ArchitectureScript.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | public class SystemSegmentWalker : ElementSegmentWalker 11 | { 12 | private readonly SoftwareSystem _system; 13 | 14 | public SystemSegmentWalker(SoftwareSystem system) : base(system) 15 | { 16 | _system = system; 17 | } 18 | 19 | [Parse("container (.*)")] 20 | public ContainerSegmentWalker AddContainer(string tag) 21 | { 22 | var container = _system.AddContainer(tag, null, null); 23 | return new ContainerSegmentWalker(container); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ArchitectureScript/Parsing/ViewSegmentWalker.cs: -------------------------------------------------------------------------------- 1 | using ArchitectureScript.Model; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ArchitectureScript.Parsing 9 | { 10 | public class ViewSegmentWalker : SegmentWalkerBase 11 | { 12 | private readonly View _view; 13 | 14 | public ViewSegmentWalker(View view) 15 | { 16 | _view = view; 17 | } 18 | 19 | [Parse("exclude (.*)")] 20 | public void AddExclusion(string tag) 21 | { 22 | _view.AddExclusion(tag); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ArchitectureScript/Program.cs: -------------------------------------------------------------------------------- 1 | using ArchitectureScript.Parsing; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace ArchitectureScript 12 | { 13 | class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | var parser = new Parser(); 18 | var model = new Model.Model(); 19 | 20 | foreach(var file in args) 21 | { 22 | var text = File.ReadAllText(file); 23 | parser.UpdateModel(model, text); 24 | } 25 | 26 | model.Normalise(); 27 | 28 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings 29 | { 30 | Formatting = Formatting.Indented, 31 | ContractResolver = new CamelCasePropertyNamesContractResolver() 32 | }; 33 | 34 | var json = JsonConvert.SerializeObject(model); 35 | 36 | Console.WriteLine(json); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ArchitectureScript/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ArchitectureScript")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ArchitectureScript")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("21c3fef1-5f7e-491d-bb9d-38f8b3827d50")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ArchitectureScript/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ArchitectureScript 2 | ================== 3 | 4 | A simple high-level DSL for defining software architecture using the C4 model described http://www.codingthearchitecture.com/2014/06/24/software_architecture_as_code.html. Generates json compatible with http://dev.structurizr.com/tryit 5 | 6 | Pass in the files you want to convert into Structurizr views 7 | 8 | A sample file might look like this: 9 | 10 | external person Anonymous User 11 | desc Anybody on the web 12 | uses kickthetable.com 13 | finds new and soon to be closing kickstarter campaigns 14 | uses Web Server 15 | finds new and soon to be closing kickstarter campaigns 16 | 17 | external person Administrator 18 | desc An authenticated user 19 | uses kickthetable.com 20 | maintains the list of kickstarter campaigns 21 | uses Web Server 22 | maintains the list of kickstarter campaigns 23 | 24 | external system Kickstarter 25 | desc kickstarter.com 26 | 27 | system kickthetable.com 28 | desc The premier way to find kickstarter campaigns in the Tabletop Games category 29 | container Web Server 30 | desc a web server 31 | tech ASP.NET MVC 4.5 on Azure Web Sites 32 | container Cache 33 | desc a cache of all of the campaigns on kickstarter in the Tabletop Games category 34 | tech Azure Caching 35 | 36 | view context kickthetable.com 37 | 38 | view containers kickthetable.com 39 | --------------------------------------------------------------------------------