<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;


namespace CodeCloner
{
  /// <summary> Destination <c>CSPROJ</c> Parser and Cloner. </summary>
  internal class DestinationCsProjParser
  {
    static List<string> ItemElementsToSkip = new List<string> {"reference","projectreference","bootstrapperpackage"};
    static List<string> ItemElementsDoNotLink = new List<string> {"folder"};
    private static XNamespace   MSBuild = "http://schemas.microsoft.com/developer/msbuild/2003";

    /// <summary> Absolute pathname of the destination <c>CSPROJ</c> including file name. </summary>
    internal  string DestCsProjAbsolutePath { get; }
    /// <summary> Absolute Directory of the destination <c>CSPROJ</c>. NO file name. </summary>
    internal  string DestCsProjDirectory { get; }
    private XDocument destCsProjXdoc;
    private XComment  startPlaceHolder;
    private XComment  endPlaceHolder;
    
    /// <summary> Source <c>CSPROJ</c>s defined in the Destination <c>CSPROJ</c> placeholder. 
    ///           Can be zero, can be lots.</summary>
    internal List<string> SourceCsProjList { get; }

    /// <summary> Code Files to be excluded from the clone. </summary>
    internal List<string> ExclusionsList { get; }

    /// <summary> Source <c>CSPROJ</c> is specified in the destination <c>CSPROJ</c> XML comment placeholder. </summary>
    /// <param name="destCsProj"> Absolute path of destination <c>CSPROJ</c>. </param>
    internal  DestinationCsProjParser(string destCsProj)
    {
      DestCsProjAbsolutePath = PathMaker.MakeAbsolutePathFromPossibleRelativePathOrDieTrying(null, destCsProj);
      DestCsProjDirectory = Path.GetDirectoryName(DestCsProjAbsolutePath)?? "";

      try { destCsProjXdoc = XDocument.Load(DestCsProjAbsolutePath); }
      catch (Exception e) { Program.Crash(e); }

      
      SourceCsProjList = new List<string>();
      ExclusionsList   = new List<string>();
      startPlaceHolder = FindCommentOrCrash("CodeCloner");
      endPlaceHolder   = FindCommentOrCrash("EndCodeCloner");
      foreach (string line in startPlaceHolder.Value.Split(new[] { "\r\n", "\n", Environment.NewLine }, StringSplitOptions.None).ToList())
      {
        
        if (line.ToLower().Trim().StartsWith("source:"))
        {
          string sourceInXml = line.ToLower().Replace("source:", "").Trim();
          string absoluteSource = PathMaker.MakeAbsolutePathFromPossibleRelativePathOrDieTrying(DestCsProjDirectory, sourceInXml);
          SourceCsProjList.Add(absoluteSource);
        }

        if (line.ToLower().Trim().StartsWith("exclude:"))
          ExclusionsList.Add(line.Replace("exclude:", "").Trim());
      }

      CloneCode();
    }

    /// <summary> Source <c>CSPROJ</c> and destination <c>CSPROJ</c> specified here. </summary>
    /// <param name="sourceCsProj"> Absolute or Relative path of Source <c>CSPROJ</c>. </param>
    /// <param name="destCsProj"> Absolute or Relative path of Destination <c>CSPROJ</c>. </param>
    internal DestinationCsProjParser(string sourceCsProj, string destCsProj)
    {
      SourceCsProjList = new List<string> {PathMaker.MakeAbsolutePathFromPossibleRelativePathOrDieTrying(null, sourceCsProj)};
      ExclusionsList   = new List<string>();
      
      DestCsProjAbsolutePath = PathMaker.MakeAbsolutePathFromPossibleRelativePathOrDieTrying(null, destCsProj);
      DestCsProjDirectory = Path.GetDirectoryName(DestCsProjAbsolutePath);

      try { destCsProjXdoc = XDocument.Load(DestCsProjAbsolutePath); }
      catch (Exception e) { Program.Crash(e); }

      startPlaceHolder = FindCommentOrCrash("CodeCloner");
      endPlaceHolder   = FindCommentOrCrash("EndCodeCloner");

      CloneCode();
    }




    /// <summary> Clones the source code from the source <c>CSPROJ</c> file to the destination <c>CSPROJ</c> file. 
    ///           Tweaks the file paths so the project can find them. 
    ///           Adds a <c>&lt;Link&gt;</c> so you can edit within the destination project.</summary>
    internal void CloneCode()
    {
      if (string.IsNullOrEmpty(DestCsProjAbsolutePath)) Program.Crash("ERROR: No destCsProjFileAbsolutePath. That's a bug.");
      if (!destCsProjXdoc.Root.Elements().Any()) Program.Crash("ERROR: No Destination CSPROJ file at " + DestCsProjAbsolutePath);

      RemoveOldDestCsProjClonedCode();

      foreach (string sourcePath in SourceCsProjList)
      {
        
        string sourceProjAbsolutePath = (PathMaker.IsAbsolutePath(sourcePath)) ? sourcePath : Path.Combine(DestCsProjDirectory, sourcePath);
        string sourceCSProjDirectory = Path.GetDirectoryName(sourceProjAbsolutePath);

        SourceCsProjParser sourceProjParser = new SourceCsProjParser(sourceProjAbsolutePath);

        endPlaceHolder.AddBeforeSelf(new XComment("Cloned from "+ sourcePath + " at " + DateTime.Now));

        // string destRelativePathPrefix = PathMaker.MakeRelativePath(sourceCSProjDirectory, DestCsProjDirectory);
        string sourceRelativePathPrefix = PathMaker.MakeRelativePath(DestCsProjDirectory, sourceCSProjDirectory);
        if (!sourceRelativePathPrefix.EndsWith("\\"))  sourceRelativePathPrefix += "\\";


        foreach (XElement sourceItemGroup in sourceProjParser.ItemGroups)
        {
          XElement destItemGroup = new XElement(MSBuild +"ItemGroup");

          foreach (XElement sourceItem in sourceItemGroup.Elements())
          {
            string elementName = sourceItem.Name.LocalName;
            if (ItemElementsToSkip.Contains(elementName.ToLower())) continue;

            XAttribute attrib = sourceItem.Attribute("Include") ?? sourceItem.Attribute("Exclude");

            if (attrib != null)
            {
              string originalPath = attrib.Value;
              if (ExclusionsList.Any(x => originalPath.Contains(x)))
              {
                Log.WriteLine("Excluded " + originalPath + " from " + sourceProjAbsolutePath + " clone to " + DestCsProjAbsolutePath
                  + " because you said Exclude: " + ExclusionsList.FirstOrDefault(x => originalPath.Contains(x)) );
                continue;
              }
              if (!PathMaker.IsAbsolutePath(originalPath))
                attrib.Value = Path.GetFullPath(sourceRelativePathPrefix + originalPath);

              IEnumerable<XElement> links = sourceItem.Descendants(MSBuild + "Link");

              if (!(links.Any() || ItemElementsDoNotLink.Contains(elementName.ToLower())))
              {
                XElement xe = new XElement(MSBuild+ "Link", originalPath);
                sourceItem.Add(xe);
              }
              destItemGroup.Add(sourceItem);
            }
          }
          if (destItemGroup.HasElements) 
          endPlaceHolder.AddBeforeSelf(destItemGroup);
        }
        endPlaceHolder.AddBeforeSelf(new XComment("End Clone from "+ sourcePath));
        Log.WriteLine("End Clone from "+ sourcePath);
      }
      destCsProjXdoc.Save(DestCsProjAbsolutePath);
    }


<commit_msg>bloody hell, it works. Well, specifying just he destination CSPROJ and embedding the source works<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;


namespace CodeCloner
{
  /// <summary> Destination <c>CSPROJ</c> Parser and Cloner. </summary>
  internal class DestinationCsProjParser
  {
    static List<string> ItemElementsToSkip = new List<string> {"reference","projectreference","bootstrapperpackage"};
    static List<string> ItemElementsDoNotLink = new List<string> {"folder"};
    private static XNamespace   MSBuild = "http://schemas.microsoft.com/developer/msbuild/2003";

    /// <summary> Absolute pathname of the destination <c>CSPROJ</c> including file name. </summary>
    internal  string DestCsProjAbsolutePath { get; }
    /// <summary> Absolute Directory of the destination <c>CSPROJ</c>. NO file name. </summary>
    internal  string DestCsProjDirectory { get; }
    private XDocument destCsProjXdoc;
    private XComment  startPlaceHolder;
    private XComment  endPlaceHolder;
    
    /// <summary> Source <c>CSPROJ</c>s defined in the Destination <c>CSPROJ</c> placeholder. 
    ///           Can be zero, can be lots.</summary>
    internal List<string> SourceCsProjList { get; }

    /// <summary> Code Files to be excluded from the clone. </summary>
    internal List<string> ExclusionsList { get; }

    /// <summary> Source <c>CSPROJ</c> is specified in the destination <c>CSPROJ</c> XML comment placeholder. </summary>
    /// <param name="destCsProj"> Absolute path of destination <c>CSPROJ</c>. </param>
    internal  DestinationCsProjParser(string destCsProj)
    {
      DestCsProjAbsolutePath = PathMaker.MakeAbsolutePathFromPossibleRelativePathOrDieTrying(null, destCsProj);
      DestCsProjDirectory = Path.GetDirectoryName(DestCsProjAbsolutePath)?? "";

      try { destCsProjXdoc = XDocument.Load(DestCsProjAbsolutePath); }
      catch (Exception e) { Program.Crash(e); }

      
      SourceCsProjList = new List<string>();
      ExclusionsList   = new List<string>();
      startPlaceHolder = FindCommentOrCrash("CodeCloner");
      endPlaceHolder   = FindCommentOrCrash("EndCodeCloner");
      foreach (string line in startPlaceHolder.Value.Split(new[] { "\r\n", "\n", Environment.NewLine }, StringSplitOptions.None).ToList())
      {
        
        if (line.ToLower().Trim().StartsWith("source:"))
        {
          string sourceInXml = line.ToLower().Replace("source:", "").Trim();
          string absoluteSource = PathMaker.MakeAbsolutePathFromPossibleRelativePathOrDieTrying(DestCsProjDirectory, sourceInXml);
          SourceCsProjList.Add(absoluteSource);
        }

        if (line.ToLower().Trim().StartsWith("exclude:"))
          ExclusionsList.Add(line.Replace("exclude:", "").Trim());
      }

      CloneCode();
    }

    /// <summary> Source <c>CSPROJ</c> and destination <c>CSPROJ</c> specified here. </summary>
    /// <param name="sourceCsProj"> Absolute or Relative path of Source <c>CSPROJ</c>. </param>
    /// <param name="destCsProj"> Absolute or Relative path of Destination <c>CSPROJ</c>. </param>
    internal DestinationCsProjParser(string sourceCsProj, string destCsProj)
    {
      SourceCsProjList = new List<string> {PathMaker.MakeAbsolutePathFromPossibleRelativePathOrDieTrying(null, sourceCsProj)};
      ExclusionsList   = new List<string>();
      
      DestCsProjAbsolutePath = PathMaker.MakeAbsolutePathFromPossibleRelativePathOrDieTrying(null, destCsProj);
      DestCsProjDirectory = Path.GetDirectoryName(DestCsProjAbsolutePath);

      try { destCsProjXdoc = XDocument.Load(DestCsProjAbsolutePath); }
      catch (Exception e) { Program.Crash(e); }

      startPlaceHolder = FindCommentOrCrash("CodeCloner");
      endPlaceHolder   = FindCommentOrCrash("EndCodeCloner");

      CloneCode();
    }




    /// <summary> Clones the source code from the source <c>CSPROJ</c> file to the destination <c>CSPROJ</c> file. 
    ///           Tweaks the file paths so the project can find them. 
    ///           Adds a <c>&lt;Link&gt;</c> so you can edit within the destination project.</summary>
    internal void CloneCode()
    {
      if (string.IsNullOrEmpty(DestCsProjAbsolutePath)) Program.Crash("ERROR: No destCsProjFileAbsolutePath. That's a bug.");
      if (!destCsProjXdoc.Root.Elements().Any()) Program.Crash("ERROR: No Destination CSPROJ file at " + DestCsProjAbsolutePath);

      RemoveOldDestCsProjClonedCode();

      foreach (string sourcePath in SourceCsProjList)
      {
        
        string sourceProjAbsolutePath = (PathMaker.IsAbsolutePath(sourcePath)) ? sourcePath : Path.Combine(DestCsProjDirectory, sourcePath);
        string SourceCsProjDirectory = Path.GetDirectoryName(sourceProjAbsolutePath);

        SourceCsProjParser sourceProjParser = new SourceCsProjParser(sourceProjAbsolutePath);

        endPlaceHolder.AddBeforeSelf(new XComment("Cloned from "+ sourcePath + " at " + DateTime.Now));

        // string destRelativePathPrefix = PathMaker.MakeRelativePath(sourceCSProjDirectory, DestCsProjDirectory);
        string sourceRelativePathPrefix = PathMaker.MakeRelativePath(DestCsProjDirectory, SourceCsProjDirectory);
        if (!sourceRelativePathPrefix.EndsWith("\\"))  sourceRelativePathPrefix += "\\";


        foreach (XElement sourceItemGroup in sourceProjParser.ItemGroups)
        {
          XElement destItemGroup = new XElement(MSBuild +"ItemGroup");

          foreach (XElement sourceItem in sourceItemGroup.Elements())
          {
            string elementName = sourceItem.Name.LocalName;
            if (ItemElementsToSkip.Contains(elementName.ToLower())) continue;

            XAttribute attrib = sourceItem.Attribute("Include") ?? sourceItem.Attribute("Exclude");

            if (attrib != null)
            {
              string originalPath = attrib.Value;
              if (ExclusionsList.Any(x => originalPath.Contains(x)))
              {
                Log.WriteLine("Excluded " + originalPath + " from " + sourceProjAbsolutePath + " clone to " + DestCsProjAbsolutePath
                  + " because you said Exclude: " + ExclusionsList.FirstOrDefault(x => originalPath.Contains(x)) );
                continue;
              }
              if (!PathMaker.IsAbsolutePath(originalPath))
              {
                string sourceAbsolutePath = Path.GetFullPath(SourceCsProjDirectory+"\\" + originalPath);
                string relativePathFromDestination = PathMaker.MakeRelativePath(DestCsProjDirectory +"\\", sourceAbsolutePath);
                attrib.Value = relativePathFromDestination;
              }

              IEnumerable<XElement> links = sourceItem.Descendants(MSBuild + "Link");

              if (!(links.Any() || ItemElementsDoNotLink.Contains(elementName.ToLower())))
              {
                XElement xe = new XElement(MSBuild+ "Link", originalPath);
                sourceItem.Add(xe);
              }
              destItemGroup.Add(sourceItem);
            }
          }
          if (destItemGroup.HasElements) 
          endPlaceHolder.AddBeforeSelf(destItemGroup);
        }
        endPlaceHolder.AddBeforeSelf(new XComment("End Clone from "+ sourcePath));
        Log.WriteLine("End Clone from "+ sourcePath);
      }
      destCsProjXdoc.Save(DestCsProjAbsolutePath);
    }



{'fraction_non_alphanumeric': 0.11112627055051504, 'fraction_numerical': 0.0005457398185415103, 'mean_word_length': 3.216278400920334, 'pattern_counts': {'":': 0, '<': 129, '<?xml version=': 0, '>': 133, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6328282', 'n_tokens_mistral': 4362, 'n_tokens_neox': 4072, 'n_words': 869}