Click here to Skip to main content
15,894,017 members
Articles / Programming Languages / C#

Calculating Metrics and Searching with a CodeDOM (Part 8)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
6 Mar 2013CDDL7 min read 22K   684   10  
Calculating metrics on and searching a CodeDOM.
// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using System;
using System.Collections.ObjectModel;
using System.IO;

using Nova.CodeDOM;
using Attribute = Nova.CodeDOM.Attribute;

namespace Nova.Analysis
{
    /// <summary>
    /// Represents a search of a CodeDOM tree for all objects of a certain <see cref="Type"/>, and the search results.
    /// </summary>
    /// <remarks>
    /// This class can be used to find all <see cref="TypeDecl"/>s, <see cref="If"/> statements, <see cref="LocalDecl"/>s,
    /// <see cref="Conditional"/> operators, etc. in the desired scope.
    /// 
    /// To perform a search, create an instance of this class, set any desired search parameters, call <see cref="Find"/>,
    /// and then look at <see cref="Results"/>.  You may bind the <see cref="Results"/> property to a UI control, and the
    /// <see cref="Find"/> method may be called on a background thread.
    /// </remarks>
    public class FindByType : IVisitor
    {
        #region /* FIELDS */

        /// <summary>
        /// The target <see cref="Type"/> of objects to be found.
        /// </summary>
        public Type TargetType;

        /// <summary>
        /// The starting <see cref="CodeObject"/> of the search, such as a <see cref="Solution"/>, <see cref="Project"/>, or <see cref="CodeUnit"/>.
        /// </summary>
        public CodeObject Scope;

        protected CodeUnit _currentCodeUnit;
        protected ObservableCollection<Result> _results = new ObservableCollection<Result>();

        #endregion

        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create an instance that searches for objects of the specified target type.
        /// </summary>
        /// <param name="targetType">The <see cref="Type"/> of objects to be found.</param>
        /// <param name="scope">The scope, or starting object, of the search.</param>
        public FindByType(Type targetType, CodeObject scope)
        {
            TargetType = targetType;
            Scope = scope;
        }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The results of the search.
        /// </summary>
        public ObservableCollection<Result> Results
        {
            get { return _results; }
        }

        /// <summary>
        /// Returns <c>true</c> for <see cref="FindByType"/>, because we want to check any hidden references.
        /// </summary>
        /// <remarks>
        /// See <see cref="CodeObject.HiddenRef"/> for more information.
        /// </remarks>
        public bool VisitHiddenRefs
        {
            get { return true; }
        }

        #endregion

        #region /* METHODS */

        /// <summary>
        /// Perform the search operation.
        /// </summary>
        public ObservableCollection<Result> Find()
        {
            if (Scope != null)
            {
                _currentCodeUnit = null;
                Scope.Accept(this);
            }
            return _results;
        }

        /// <summary>
        /// Check if the <see cref="CodeObject"/> is a match.
        /// </summary>
        protected void Check(CodeObject codeObject)
        {
            if (TargetType.IsInstanceOfType(codeObject))
            {
                // Determine the current CodeUnit if it isn't set (meaning we started visiting below the CodeUnit level)
                if (_currentCodeUnit == null)
                    _currentCodeUnit = codeObject.FindParent<CodeUnit>();

                // Ignore generated objects
                if (!codeObject.IsGenerated)
                    _results.Add(new Result(codeObject, _currentCodeUnit));
            }
        }

        public override string ToString()
        {
            string scope;
            if (Scope is Solution || Scope is Project)
                scope = Path.GetFileName(((IFile)Scope).FileName);
            else if (Scope is INamedCodeObject)
                scope = ((INamedCodeObject)Scope).Name;
            else if (Scope == null)
                scope = "NULL";
            else
                scope = "code fragment";
            return "Find objects of type '" + TargetType.Name + "' in " + scope;
        }

        #endregion

        #region /* IVISITOR IMPLEMENTATION */

        /// <summary>Visit a <see cref="Project"/>.</summary>
        public void Visit(Project project)
        {
            Check(project);
        }

        /// <summary>Visit a <see cref="CodeUnit"/>.</summary>
        public void Visit(CodeUnit codeUnit)
        {
            _currentCodeUnit = codeUnit;
            Check(codeUnit);
        }

        /// <summary>Visit a <see cref="UsingDirective"/> statement.</summary>
        public void Visit(UsingDirective usingDirective)
        {
            Check(usingDirective);
        }

        /// <summary>Visit an <see cref="Alias"/> statement.</summary>
        public void Visit(Alias alias)
        {
            Check(alias);
        }

        /// <summary>Visit an <see cref="ExternAlias"/> statement.</summary>
        public void Visit(ExternAlias externAlias)
        {
            Check(externAlias);
        }

        /// <summary>Visit a <see cref="Break"/> statement.</summary>
        public void Visit(Break @break)
        {
            Check(@break);
        }

        /// <summary>Visit a <see cref="Continue"/> statement.</summary>
        public void Visit(Continue @continue)
        {
            Check(@continue);
        }

        /// <summary>Visit a <see cref="Goto"/> statement.</summary>
        public void Visit(Goto @goto)
        {
            Check(@goto);
        }

        /// <summary>Visit a <see cref="Label"/> statement.</summary>
        public void Visit(Label label)
        {
            Check(label);
        }

        /// <summary>Visit a <see cref="Return"/> statement.</summary>
        public void Visit(Return @return)
        {
            Check(@return);
        }

        /// <summary>Visit a <see cref="Throw"/> statement.</summary>
        public void Visit(Throw @throw)
        {
            Check(@throw);
        }

        /// <summary>Visit a <see cref="YieldStatement"/> statement.</summary>
        public void Visit(YieldStatement yieldStatement)
        {
            Check(yieldStatement);
        }

        /// <summary>Visit a <see cref="FieldDecl"/> statement (also <see cref="MultiFieldDecl"/>).</summary>
        public void Visit(FieldDecl fieldDecl)
        {
            Check(fieldDecl);
        }

        /// <summary>Visit a <see cref="LocalDecl"/> statement (also <see cref="MultiLocalDecl"/>).</summary>
        public void Visit(LocalDecl localDecl)
        {
            Check(localDecl);
        }

        /// <summary>Visit a <see cref="ParameterDecl"/> statement (also <see cref="ValueParameterDecl"/>).</summary>
        public void Visit(ParameterDecl parameterDecl)
        {
            Check(parameterDecl);
        }

        /// <summary>Visit an <see cref="EnumMemberDecl"/> statement.</summary>
        public void Visit(EnumMemberDecl enumMemberDecl)
        {
            Check(enumMemberDecl);
        }

        /// <summary>Visit an <see cref="IfBase"/> statement (common base of <see cref="If"/>, <see cref="ElseIf"/>).</summary>
        public void Visit(IfBase ifBase)
        {
            Check(ifBase);
        }

        /// <summary>Visit an <see cref="Else"/> statement.</summary>
        public void Visit(Else @else)
        {
            Check(@else);
        }

        /// <summary>Visit a <see cref="Switch"/> statement.</summary>
        public void Visit(Switch @switch)
        {
            Check(@switch);
        }

        /// <summary>Visit a <see cref="SwitchItem"/> statement (common base of <see cref="Case"/>, <see cref="Default"/>).</summary>
        public void Visit(SwitchItem switchItem)
        {
            Check(switchItem);
        }

        /// <summary>Visit a <see cref="For"/> statement.</summary>
        public void Visit(For @for)
        {
            Check(@for);
        }

        /// <summary>Visit a <see cref="ForEach"/> statement.</summary>
        public void Visit(ForEach forEach)
        {
            Check(forEach);
        }

        /// <summary>Visit a <see cref="While"/> statement.</summary>
        public void Visit(While @while)
        {
            Check(@while);
        }

        /// <summary>Visit a <see cref="Try"/> statement.</summary>
        public void Visit(Try @try)
        {
            Check(@try);
        }

        /// <summary>Visit a <see cref="Catch"/> statement.</summary>
        public void Visit(Catch @catch)
        {
            Check(@catch);
        }

        /// <summary>Visit a <see cref="Finally"/> statement.</summary>
        public void Visit(Finally @finally)
        {
            Check(@finally);
        }

        /// <summary>Visit a <see cref="Using"/> statement.</summary>
        public void Visit(Using @using)
        {
            Check(@using);
        }

        /// <summary>Visit a <see cref="Lock"/> statement.</summary>
        public void Visit(Lock @lock)
        {
            Check(@lock);
        }

        /// <summary>Visit a <see cref="BlockDecl"/> statement.</summary>
        public void Visit(BlockDecl blockDecl)
        {
            Check(blockDecl);
        }

        /// <summary>Visit a <see cref="CheckedBlock"/> statement.</summary>
        public void Visit(CheckedBlock checkedBlock)
        {
            Check(checkedBlock);
        }

        /// <summary>Visit an <see cref="UncheckedBlock"/> statement.</summary>
        public void Visit(UncheckedBlock uncheckedBlock)
        {
            Check(uncheckedBlock);
        }

        /// <summary>Visit a <see cref="NamespaceDecl"/> statement (does NOT include <see cref="CodeUnit"/>s).</summary>
        public void Visit(NamespaceDecl namespaceDecl)
        {
            Check(namespaceDecl);
        }

        /// <summary>
        /// Visit a <see cref="TypeDecl"/> statement (common base of <see cref="ClassDecl"/>, <see cref="StructDecl"/>,
        /// <see cref="InterfaceDecl"/>, <see cref="EnumDecl"/>, <see cref="DelegateDecl"/>).
        /// </summary>
        public void Visit(TypeDecl typeDecl)
        {
            Check(typeDecl);
        }

        /// <summary>
        /// Visit a <see cref="MethodDeclBase"/> statement (common base of <see cref="MethodDecl"/>, <see cref="GenericMethodDecl"/>,
        /// <see cref="ConstructorDecl"/>, <see cref="DestructorDecl"/>, <see cref="GetterDecl"/>, <see cref="SetterDecl"/>, <see cref="AdderDecl"/>,
        /// <see cref="RemoverDecl"/>, <see cref="OperatorDecl"/>, <see cref="ConversionOperatorDecl"/>).
        /// </summary>
        public void Visit(MethodDeclBase methodDeclBase)
        {
            Check(methodDeclBase);
        }

        /// <summary>Visit a <see cref="PropertyDeclBase"/> statement (common base of <see cref="PropertyDecl"/>, <see cref="IndexerDecl"/>, <see cref="EventDecl"/>).</summary>
        public void Visit(PropertyDeclBase propertyDeclBase)
        {
            Check(propertyDeclBase);
        }

        /// <summary>Visit an <see cref="AnonymousMethod"/> expression.</summary>
        public void Visit(AnonymousMethod anonymousMethod)
        {
            Check(anonymousMethod);
        }

        /// <summary>Visit an <see cref="Initializer"/> expression.</summary>
        public void Visit(Initializer initializer)
        {
            Check(initializer);
        }

        /// <summary>Visit a <see cref="Literal"/> expression.</summary>
        public void Visit(Literal literal)
        {
            Check(literal);
        }

        /// <summary>
        /// Visit a <see cref="SymbolicRef"/> expression (common base of <see cref="NamespaceRef"/>,
        /// <see cref="TypeRefBase"/> [common base of <see cref="TypeRef"/>, <see cref="MethodRef"/>, <see cref="UnresolvedRef"/>],
        /// <see cref="VariableRef"/>, <see cref="SelfRef"/>, <see cref="GotoTargetRef"/>, <see cref="ExternAliasRef"/>,
        /// and <see cref="DirectiveSymbolRef"/>).
        /// </summary>
        public void Visit(SymbolicRef symbolicRef)
        {
            Check(symbolicRef);
        }

        /// <summary>Visit an <see cref="Unrecognized"/> expression.</summary>
        public void Visit(Unrecognized unrecognized)
        {
            Check(unrecognized);
        }

        /// <summary>Visit an <see cref="ArgumentsOperator"/> operator (common base of <see cref="Call"/>, <see cref="Index"/>, <see cref="NewOperator"/>).</summary>
        public void Visit(ArgumentsOperator argumentsOperator)
        {
            Check(argumentsOperator);
        }

        /// <summary>
        /// Visit a <see cref="SingleArgumentOperator"/> operator (common base of <see cref="Ref"/>, <see cref="Out"/>,
        /// <see cref="Checked"/>, <see cref="Unchecked"/>, <see cref="TypeOf"/>, <see cref="SizeOf"/>, <see cref="DefaultValue"/>).
        /// </summary>
        public void Visit(SingleArgumentOperator singleArgumentOperator)
        {
            Check(singleArgumentOperator);
        }

        /// <summary>
        /// Visit a <see cref="BinaryOperator"/> operator (common base of <see cref="BinaryArithmeticOperator"/>, <see cref="BinaryBitwiseOperator"/>,
        /// <see cref="BinaryBooleanOperator"/>, <see cref="BinaryShiftOperator"/>, <see cref="Assignment"/>, <see cref="As"/>, <see cref="Dot"/>,
        /// <see cref="IfNullThen"/>, <see cref="Lookup"/>).
        /// </summary>
        public void Visit(BinaryOperator binaryOperator)
        {
            Check(binaryOperator);
        }

        /// <summary>
        /// Visit an <see cref="UnaryOperator"/> operator (common base of <see cref="Cast"/>, <see cref="Complement"/>,
        /// <see cref="Decrement"/>, <see cref="Increment"/>, <see cref="Negative"/>, <see cref="Not"/>,
        /// <see cref="Positive"/>, <see cref="PostIncrement"/>, <see cref="PostDecrement"/>).
        /// </summary>
        public void Visit(UnaryOperator unaryOperator)
        {
            Check(unaryOperator);
        }

        /// <summary>Visit a <see cref="Conditional"/> operator.</summary>
        public void Visit(Conditional conditional)
        {
            Check(conditional);
        }

        /// <summary>Visit an <see cref="CodeDOM.Attribute"/> annotation.</summary>
        public void Visit(Attribute attribute)
        {
            Check(attribute);
        }

        /// <summary>Visit a <see cref="Comment"/> annotation.</summary>
        public void Visit(Comment comment)
        {
            Check(comment);
        }

        /// <summary>
        /// Visit a <see cref="DocComment"/> annotation (also <see cref="DocText"/>, <see cref="DocCodeRefBase"/>, <see cref="DocNameBase"/>,
        /// <see cref="DocB"/>, <see cref="DocC"/>, <see cref="DocCode"/>, <see cref="DocCDATA"/>, <see cref="DocExample"/>, <see cref="DocI"/>,
        /// <see cref="DocInclude"/>, <see cref="DocPara"/>, <see cref="DocRemarks"/>, <see cref="DocSummary"/>, <see cref="DocTag"/>,
        /// <see cref="DocValue"/>, <see cref="DocList"/>, <see cref="DocListHeader"/>, <see cref="DocListItem"/>, <see cref="DocListDescription"/>,
        /// <see cref="DocListTerm"/>).
        /// </summary>
        public void Visit(DocComment docComment)
        {
            Check(docComment);
        }

        /// <summary>
        /// Visit a <see cref="CompilerDirective"/> annotation (common base of <see cref="ConditionalDirective"/>, <see cref="MessageDirective"/>,
        /// <see cref="SymbolDirective"/>, <see cref="PragmaDirective"/>, <see cref="LineDirective"/>).
        /// </summary>
        public void Visit(CompilerDirective compilerDirective)
        {
            Check(compilerDirective);
        }

        /// <summary>Visit a <see cref="Message"/> annotation.</summary>
        public void Visit(Message message)
        {
            Check(message);
        }

        /// <summary>Visit a <see cref="TypeParameter"/>.</summary>
        public void Visit(TypeParameter typeParameter)
        {
            Check(typeParameter);
        }

        /// <summary>Visit a <see cref="ConstraintClause"/>.</summary>
        public void Visit(ConstraintClause constraintClause)
        {
            Check(constraintClause);
        }

        /// <summary>
        /// Visit a <see cref="TypeParameterConstraint"/> (common base of <see cref="ClassConstraint"/>, <see cref="StructConstraint"/>,
        /// <see cref="NewConstraint"/>, <see cref="TypeConstraint"/>).
        /// </summary>
        public void Visit(TypeParameterConstraint typeParameterConstraint)
        {
            Check(typeParameterConstraint);
        }

        /// <summary>
        /// Visit a <see cref="Block"/> body of a <see cref="BlockStatement"/> - this should rarely be used, since
        /// all members of the Block will be visited separately.
        /// </summary>
        public void Visit(Block block)
        { }

        /// <summary>
        /// Visit a <see cref="ChildList{T}"/> collection - not a <see cref="CodeObject"/> itself, so not visited here.
        /// </summary>
        public void Visit<T>(ChildList<T> childList) where T : CodeObject
        { }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

Comments and Discussions