// 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
}
}