PoshCode Archive  Artifact [2d7fb9ba78]

Artifact 2d7fb9ba78fb4c3c00edc3a37802ca699dfc98ec7a01cd2628ba8411eba15038:

  • File Add-Namespace.ps1 — part of check-in [6e1e8e5aee] at 2018-06-10 13:03:22 on branch trunk — Version 1.0 (user: George Howarth size: 9408)

# encoding: ascii
# api: powershell
# title: Add-Namespace
# description: Version 1.0
# version: 0.1
# type: script
# author: George Howarth
# license: CC0
# function: Add-Namespace
# x-poshcode-id: 1964
# x-archived: 2015-06-22T09:29:23
# x-published: 2011-07-09T14:50:00
#
# Imports the types in the specified namespaces in the specified assemblies.
# I plan to support -WhatIf in the next version.
# Just for clarfication, THIS is the lastest version. Apologies for the multi-post.
# Special thanks to Oisin Grehan for his great work
#
trap [System.Management.Automation.RuntimeException]
{
    $entryException = $_
    
    if ($_.CategoryInfo.Category -eq [System.Management.Automation.ErrorCategory]::InvalidOperation)
    {
        if ($_.FullyQualifiedErrorId -eq "TypeNotFound")
        {
            $targetName = $_.CategoryInfo.TargetName
            
            try
            {
                $isAmbiguous = $global:__ambiguousTypeNames.Contains($targetName)
            }
            catch
            {
                throw $entryException
            }
            
            if ($isAmbiguous)
            {
                $message = New-Object System.Text.StringBuilder
                $message.AppendFormat("The type [{0}] is ambiguous. Specify one of the following: ", $targetName).AppendLine() | Out-Null
                
                [System.Type]::GetType("System.Management.Automation.TypeAccelerators")::Get.GetEnumerator() | ForEach-Object {
                    if (($_.Key.Split('.'))[-1] -eq $targetName)
                    {
                        $message.Append($_.Key).AppendLine() | Out-Null
                    }
                }
                
                $exception = New-Object System.Management.Automation.RuntimeException -ArgumentList $message.ToString()
                $errorId = "TypeNotFound"
                $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidOperation
                $targetObject = $_.TargetObject
                
                throw New-Object System.Management.Automation.ErrorRecord -ArgumentList $exception, $errorId, $errorCategory, $targetObject
            }
        }
    }
}

<#
    .SYNOPSIS
        Imports the types in the specified namespaces in the specified assemblies.

    .DESCRIPTION
        The Add-Namespace function adds a type accelerator for each type found in the specified namespaces in the specified assemblies that satisfy a set of conditions. For more information see the NOTES section.

    .PARAMETER Assembly
        The namespace to import.

    .PARAMETER Namespace
        Specifies one or more namespaces to import.

    .INPUTS
        System.Reflection.Assembly
            You can pipe an assembly to Add-Namespace.

    .OUTPUTS
        None
            This function does not return any output.

    .NOTES
        The type accelerator for the type is added if the type:
        
        - Has a base type which is not System.Attribute, System.Delegate or System.MulticastDelegate
        - Is not abstract
        - Is not an interface
        - Is not nested
        - Is public
        - Is visible
        - Is qualified by the namespace specified in the Namespace parameter
        
        This function also comes with an exception handler in the form of a trap block. Type name collisions occur when a type has the same name of another type which is in a different namespace. When this happens, the function adds or replaces the type accelerator for that type using its fully-qualified type name. If a type resolution occurs during runtime, the trap block will determine if the type was unresolved during any of the calls made to Add-Namespace and throw an exception listing valid replacements.
        
        The type accelerators added by this function exist only in the current session. To use the type accelerators in all sessions, add them to your Windows PowerShell profile. For more information about the profile, see about_profiles.
        
        Be aware that namspaces can span multiple assemblies, in which case you would have to import the namespace for each assembly that it exists in.
        
        This function will not attempt to add or replace types which already exist under the same name.
    
    .EXAMPLE
        C:\PS> [System.Reflection.Assembly]::LoadWithPartialName("mscorlib") | Add-Namespace System.Reflection
        
        C:\PS> [Assembly]::LoadWithPartialName("System.Windows.Forms")
        
        This example shows how to import namespaces from an assembly. The assembly must be loaded non-reflectively into the current application domain.
    
    .EXAMPLE
        C:\PS> $assemblies = @([System.Reflection.Assembly]::LoadWithPartialName("mscorlib"), [System.Reflection.Assembly]::LoadWithPartialName("System"), [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms"), [System.Reflection.Assembly]::LoadWithPartialName("System.Xml"), [System.Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq"))
        
        C:\PS> $assemblies | Add-Namespace System, System.Collections, System.Collections.Generic, System.Net, System.Net.NetworkInformation, System.Reflection, System.Windows.Forms, System.Xml.Linq
        
        This example shows how to import multiple namespaces from multiple assemblies.   
    
    .LINK
        about_trap
#>
function Add-Namespace
{
    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        [Parameter(ValueFromPipeline = $true)]
        [System.Reflection.Assembly]$Assembly,
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [String[]]$Namespace
    )
    
    BEGIN
    {
        if ($global:__ambiguousTypeNames -eq $null)
        { 
            $global:__ambiguousTypeNames = New-Object 'System.Collections.Generic.List[System.String]'
        }
        
        $genericRegex = [Regex]'(?<Name>.*)`\d+'
        
        $typeAccelerators = [System.Type]::GetType("System.Management.Automation.TypeAccelerators")
        $typeDictionary = $typeAccelerators::Get
    }
    
    PROCESS
    {
        $_.GetTypes() | Where-Object { 
            ($_.BaseType -ne [System.Attribute]) -and 
            ($_.BaseType -ne [System.Delegate]) -and 
            ($_.BaseType -ne [System.MulticastDelegate]) -and 
            !$_.IsAbstract -and
            !$_.IsInterface -and 
            !$_.IsNested -and 
            $_.IsPublic -and 
            $_.IsVisible -and 
            ($Namespace -contains $_.Namespace)
        } | ForEach-Object { 
            $name = $_.Name
            $fullName = $_.FullName
        
            if ($_.IsGenericType)
            {
                if ($_.Name -match $genericRegex)
                {
                    $name = $Matches["Name"]
                }
                
                if ($_.FullName -match $genericRegex)
                {
                    $fullName = $Matches["Name"]
                }
            }
            
            if ($typeDictionary.ContainsKey($name))
            {
                if ($typeDictionary[$name] -eq $_)
                {
                    return
                }
            }
            
            if ($typeDictionary.ContainsKey($fullName))
            {
                if ($typeDictionary[$fullName] -eq $_)
                {
                    return
                }
            }
            
            if ($global:__ambiguousTypeNames.Contains($name))
            {
                $typeAccelerators::Add($fullName, $_)
                return
            }
            
            if ($typeDictionary.ContainsKey($name))
            {
                $type = $typeDictionary[$name]
                
                if ($_ -ne $type)
                {
                    $global:__ambiguousTypeNames.Add($name)
                    
                    $newName = $typeDictionary[$name].FullName
                    
                    if ($newName -match $genericRegex)
                    {
                        $newName = $Matches["Name"]
                    }
                    
                    $typeAccelerators::Remove($name)
                    $typeAccelerators::Add($newName, $type)
                    
                    $typeAccelerators::Add($fullName, $_)
                }
                
                return
            }
            
            $typeAccelerators::Add($name, $_)
        } | Out-Null
    }
    
    END
    {
        
    }
}

# Sample usage
# You can do this as an initialization task for your script

@(
    [System.Reflection.Assembly]::LoadWithPartialName("mscorlib"),
    [System.Reflection.Assembly]::LoadWithPartialName("System"),
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms"),
    [System.Reflection.Assembly]::LoadWithPartialName("System.Xml"),
    [System.Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") 
) | Add-Namespace -Namespace `
    System, 
    System.Collections, 
    System.Collections.Generic, 
    System.Net, 
    System.Net.NetworkInformation, 
    System.Reflection, 
    System.Windows.Forms,
    System.Xml.Linq