PowerShell: AL-Objektsplitter

14. August 2019 17:02

AL-Dateien werden ja normalerweise einzeln erzeugt, aber wenn man zu Abgleichszwecken daraus ein Objektpaket zusammenstellt, sind anschließend die alten C/AL-Objektsplitter natürlich nicht mehr verwendbar, wenn die wieder zerlegt werden soll.

Mit diesem Skript ist das machbar (zumindest für die AL-Dateien aus unserem Add-on, die alle noch eine Objekt-ID enthalten, funktioniert es einwandfrei :-) , falls mit anderen Dateien Fehler auftauchen, bitte Beispiele reinstellen).

Die Dateien werden ausgehehend von dem Ordner, wo das Objektpaket liegt (das kann über ein Fenster ausgewählt werden)…
OpenALFile.png
…in einem Unterordner SPLITBCOBJ erzeugt.

Bei der Dateinamenstruktur für AL-Dateien kursieren ja mittlerweile verschiedene Varianten.
Im Skript werden weitgehend die Namensformatierungen des txt2Al-Tools verwendet (nicht, weil ich die schön finde, sondern weil sie eben so sind, wie sie sind :mrgreen: ), nur bei Extensions (für Tables und Pages) fehlt die Nummer des Objekts, auf das die Extension angewandt wird, weil sich dieses aus dem Dateiinhalt nicht entnehmen lässt. Ansonsten sind andere Namensstrukturen natürlich im Skript nach Bedarf anpassbar.

ALObjectSplitter.png


Code:
function Split-ALObjectFile
{
  $ErrorActionPreference = "Stop"
  $PSDefaultParameterValues['*:ErrorAction']='Stop'
  Function Get-FileName($initialDirectory)
  {
    [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
   
    $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $OpenFileDialog.initialDirectory = $initialDirectory
    $OpenFileDialog.filter = "AL Object Files (*.al)|*.al"
    $OpenFileDialog.ShowDialog() | Out-Null
    $OpenFileDialog.filename
  }
       
  $inputfile = Get-FileName "C:\temp" # This is the default path in OpenFile window.
  if ($inputfile -eq "") {throw 'Please select a file'}
     
  [decimal]$filesize = ((Get-Item $inputfile).length/1MB)
  $filesize =[math]::round($filesize,2)
  $inputfile = resolve-path $inputfile
  $WorkingFolder = Split-Path -Parent $inputfile
  if (Test-path "$WorkingFolder\SPLITBCOBJ\")
    {Remove-Item -path "$WorkingFolder\SPLITBCOBJ\" -Recurse -Force}


  Write-Host "Splitting NAV objects from $inputfile (Size: $filesize MB) to $WorkingFolder\SPLITBCOBJ\, this may take a while..." -ForegroundColor Yellow
  $starttime = date
   
  $Sr = new-object System.IO.StreamReader($inputfile,[system.text.encoding]::GetEncoding(65001))
  [int]$ObjCnt = 0
  [int]$TabCnt = 0
  [int]$PagCnt = 0
  [int]$RepCnt = 0
  [int]$CodCnt = 0
  [int]$XmlCnt = 0
  [int]$QueCnt = 0
  [int]$TabExtCnt = 0
  [int]$PagExtCnt = 0
  [int]$EnumCnt = 0
  [int]$EnumExtCnt = 0
  [int]$CtrlAddInCnt = 0
  [int]$PagCstmCnt = 0
  [int]$KeysCnt = 0
  [int]$DotNetCnt = 0
  [int]$ProfileCnt = 0

  while (-not $Sr.EndOfStream)
  {
    $Currline = $sr.ReadLine()
       
    if ($Currline.StartsWith('table ')) {$NewObject = $True}
    Elseif ($Currline.StartsWith('tableextension ')) {$NewObject = $True}
    Elseif  ($Currline.StartsWith('page ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('pageextension ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('pagecustomization ')) {$NewObject = $True}
    Elseif  ($Currline.StartsWith('report ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('query ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('codeunit ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('enum ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('enumextension ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('keys ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('profile ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('xmlport ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('dotnet ')) {$NewObject = $True}
    Elseif   ($Currline.StartsWith('controladdin ')) {$NewObject = $True}
    else {$NewObject = $false}
         

       
    if ($NewObject)
    {
      $IsExtension = $false
      $dblquotes = [char]34
      [String]$ObjectChars = $Currline.Substring(0,5)
      [String]$ObjectChar = $Currline.Substring(6,1)
         
      write-host $currline
      $IsExtension = ($Currline.indexof(' extends ') -gt 0)
      if ($IsExtension)
      {
        $PosExtends = $Currline.indexof(' extends ')
        if ($PosExtends -gt 0)
        {
          $ObjectExtendedName = $Currline.Substring($PosExtends+9)
          $CleanObjectExtendedName = $ObjectExtendedName.TrimStart('"')
          $CleanObjectExtendedName = $CleanObjectExtendedName.TrimEnd(' ')
          $CleanObjectExtendedName = $CleanObjectExtendedName.TrimEnd('"')
          $CleanObjectExtendedName = $CleanObjectExtendedName.Trim('/')     
        }
        else
        {$CleanObjectExtendedName = ''}
      }
      else
      {$CleanObjectExtendedName = ''}
         
      #write-host $ObjectChars
      #write-host $ObjectChar
      $PosFirstBlank =  $Currline.indexof(' ')
      $Currline2 = $Currline.Substring($PosFirstBlank+1)
      $PosSecondBlank =  $Currline2.indexof(' ')
      if ($PosSecondBlank -ne 0)
      {$ObjectName = $Currline2.Substring($PosSecondBlank+1)}
      else
      {$ObjectName = ''}
         
      if ($ObjectName.Startswith('"'))
      {
        $CleanObjectName = $ObjectName.TrimStart('"')
        $CleanObjectName = $CleanObjectName.TrimEnd(' ')
        $CleanObjectName = $CleanObjectName.TrimEnd('"')
        $CleanObjectName = $CleanObjectName.Trim('/')
      }
      else
      {$CleanObjectName = $ObjectName.Trim('/')}
         
      write-host $currline2
      $ObjectLine = $currline.Split(' ')
      $ObjCnt++
      Switch ($ObjectChars)
      {
        'table' {
          if (!$IsExtension)                 
          {
            $TabCnt++;$ObjectType = 'Table'
          }
          else
          {
            $TabExtCnt++;$ObjectType = 'TableExt'
          }
                         
        }
             
        'page ' {
        $PagCnt++;$ObjectType = 'Page'}
 
        'pagee' {$PagExtCnt++; $ObjectType = 'PageExt'}
        'pagec' {$PagCstmCnt++;$ObjectType = 'PageCstm'}
        'repor' {$RepCnt++;$ObjectType = 'Report'}
        'codeu'{$CodCnt++;$ObjectType = 'Codeunit'}
        'xmlpo' {$XmlCnt++;$ObjectType = 'XMLport'}
        'enum ' {$enumCnt++;$ObjectType = 'Enum'}
        'enume' {$enumCnt++;$ObjectType = 'EnumExt'}
        'keys ' {$KeysCnt++;$ObjectType = 'Keys'}
        'profi' {$ProfileCnt++;$ObjectType = 'Profile'}
        'query ' {$QueCnt++;$ObjectType = 'Query'}
        'dotnet ' {$DotNetCnt++;$ObjectType = 'DotNet'}
               
        'controladdin ' {$CtrlAddinCnt++;$ObjectType = 'ControlAddin'}
       
      }

           
         
      if (!$IsExtension)
      {
        If (($ObjectLine[2] -eq '') -and ($CleanObjectName -eq ''))
               
        {$ObjectFileName = $ObjectType + '.al'}
           
        elseif (($ObjectLine[1] -ne '') -and ($CleanObjectName -eq ''))
           
        {$ObjectFileName = $ObjectType + $ObjectLine[1] + '.al'}
           
        elseif (($ObjectLine[1] -ne '') -and ($CleanObjectName -eq ''))
           
        {$ObjectFileName = $ObjectType + $ObjectLine[1] + $CleanObjectName + '.al'}
           
        else
        {$ObjectFileName = $ObjectType + ' ' + $ObjectLine[1] + ' - ' + $CleanObjectName + '.al'}
      }
      else
      {
             
             
        $ObjectFileName = 'Modification  - ' + $CleanObjectExtendedName + '(' + $ObjectType + ')' + '.al'
      }
           
           
      #write-host "Object Type: $Objecttype"   
      #write-host "Object Name: $CleanObjectName"
      #write-host 'Object ID: ' $ObjectLine[1]
      #write-host "Object Extended Name: $CleanObjectExtendedName"
      $ObjectFileName = $ObjectFileName.replace('/','')
      write-host "File Name: $ObjectFileName"
      $Objectfile = New-Item -path "$WorkingFolder\SPLITBCOBJ\$ObjectFileName" -type file -force
           
      $sw = new-object System.IO.Streamwriter($Objectfile,$false,[system.text.encoding]::GetEncoding(65001))
      #IF (Test-Path $ObjectFile) {Remove-Item $ObjectFile}
       
    }
       
    if (-not $Currline.StartsWith('}'))
    {$sw.writeline($Currline)}
    else
    {
      $sw.writeline($Currline)
      $sw.writeline()
      $sw.Flush()

    }

       
  }
   
  $endtime = date
  $time = $endtime - $starttime
  Write-Host "$ObjCnt NAV objects splitted to $WorkingFolder\SPLITBCOBJ\ in $($time.Minutes)m:$($time.Seconds)s:$($time.Milliseconds)ms" -ForegroundColor Yellow
  Write-Host "Tables: $TabCnt" -ForegroundColor Yellow
  Write-Host "Table Extensions: $TabExtCnt" -ForegroundColor Yellow
  Write-Host "Pages: $PagCnt" -ForegroundColor Yellow
  Write-Host "Page Extensions: $PagExtCnt" -ForegroundColor Yellow
  Write-Host "Page Customizations: $PagCstmCnt" -ForegroundColor Yellow
  Write-Host "Reports: $RepCnt" -ForegroundColor Yellow
  Write-Host "Codeunits: $CodCnt" -ForegroundColor Yellow
  Write-Host "XMLPorts: $XMLCnt" -ForegroundColor Yellow
  Write-Host "Queries: $QueCnt" -ForegroundColor Yellow
  Write-Host "Enums: $EnumCnt" -ForegroundColor Yellow
  Write-Host "Enum Extensions: $EnumExtCnt" -ForegroundColor Yellow
  Write-Host "Keys: $KeysCnt" -ForegroundColor Yellow
  Write-Host "ControlAddin: $CtrlAddinCnt" -ForegroundColor Yellow
  Write-Host "Dotnet: $DotnetCnt" -ForegroundColor Yellow
  Write-Host "Profiles: $ProfileCnt" -ForegroundColor Yellow
   
 
  $sr.Close()
  $sr.Dispose()
  $sw.close()
  $sw.Dispose()
  Invoke-item "$WorkingFolder\SPLITBCOBJ\"
}
Split-ALObjectFile


AL-Pakete lassen sich so erzeugen (hier als Beispiel in C:\Temp)
Code:
$WorkingFolder = 'C:\Temp'
    Get-Content $WorkingFolder\*.al  -encoding UTF8 | Out-File $WorkingFolder\allobjects.al -encoding UTF8
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.