Port ManifestReader from NuGet.Core

This commit is contained in:
Eric St. John 2016-01-13 09:49:12 -08:00
parent f3f6a2bf52
commit 7087335d41
3 changed files with 360 additions and 174 deletions

View file

@ -14,14 +14,24 @@ namespace NuGet
{
private const string SchemaVersionAttributeName = "schemaVersion";
public Manifest(ManifestMetadata metadata)
public Manifest(ManifestMetadata metadata) : this(metadata, null)
{
}
public Manifest(ManifestMetadata metadata, ICollection<ManifestFile> files)
{
if (metadata == null)
{
throw new ArgumentNullException(nameof(metadata));
}
Metadata = metadata;
if (files != null)
{
Files = files;
}
}
public ManifestMetadata Metadata { get; }
@ -76,7 +86,7 @@ namespace NuGet
XDocument document = XDocument.Load(stream);
var schemaNamespace = GetSchemaNamespace(document);
return document.Root.ReadManifest(schemaNamespace);
return ManifestReader.ReadManifest(document);
}
private static string GetSchemaNamespace(XDocument document)

View file

@ -0,0 +1,310 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using NuGet.Frameworks;
using NuGet.Packaging.Core;
using NuGet.Versioning;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
// TODO: Resources using NuGet.Resources;
namespace NuGet
{
internal static class ManifestReader
{
private static readonly string[] RequiredElements = new string[] { "id", "version", "authors", "description" };
public static Manifest ReadManifest(XDocument document)
{
var metadataElement = document.Root.ElementsNoNamespace("metadata").FirstOrDefault();
if (metadataElement == null)
{
// TODO: Resources
throw new InvalidDataException(
String.Format(CultureInfo.CurrentCulture, "NuGetResources.Manifest_RequiredElementMissing {0}", "metadata"));
}
return new Manifest(ReadMetadata(metadataElement),
ReadFilesList(document.Root.ElementsNoNamespace("files").FirstOrDefault()));
}
private static ManifestMetadata ReadMetadata(XElement xElement)
{
var manifestMetadata = new ManifestMetadata();
manifestMetadata.DependencySets = new List<PackageDependencySet>();
manifestMetadata.PackageAssemblyReferences = new List<PackageReferenceSet>();
manifestMetadata.MinClientVersionString = xElement.GetOptionalAttributeValue("minClientVersion");
// we store all child elements under <metadata> so that we can easily check for required elements.
var allElements = new HashSet<string>();
XNode node = xElement.FirstNode;
while (node != null)
{
var element = node as XElement;
if (element != null)
{
ReadMetadataValue(manifestMetadata, element, allElements);
}
node = node.NextNode;
}
// now check for required elements, which include <id>, <version>, <authors> and <description>
foreach (var requiredElement in RequiredElements)
{
if (!allElements.Contains(requiredElement))
{
// TODO: Resources
throw new InvalidDataException(
String.Format(CultureInfo.CurrentCulture, "NuGetResources.Manifest_RequiredElementMissing {0}", requiredElement));
}
}
return manifestMetadata;
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
private static void ReadMetadataValue(ManifestMetadata manifestMetadata, XElement element, HashSet<string> allElements)
{
if (element.Value == null)
{
return;
}
allElements.Add(element.Name.LocalName);
string value = element.Value?.Trim();
switch (element.Name.LocalName)
{
case "id":
manifestMetadata.Id = value;
break;
case "version":
manifestMetadata.Version = NuGetVersion.Parse(value);
break;
case "authors":
manifestMetadata.Authors = value.Split(',').Select(a => a.Trim());
break;
case "owners":
manifestMetadata.Owners = value.Split(',').Select(a => a.Trim());
break;
case "licenseUrl":
manifestMetadata.LicenseUrl = new Uri(value);
break;
case "projectUrl":
manifestMetadata.ProjectUrl = new Uri(value);
break;
case "iconUrl":
manifestMetadata.IconUrl = new Uri(value);
break;
case "requireLicenseAcceptance":
manifestMetadata.RequireLicenseAcceptance = XmlConvert.ToBoolean(value);
break;
case "developmentDependency":
manifestMetadata.DevelopmentDependency = XmlConvert.ToBoolean(value);
break;
case "description":
manifestMetadata.Description = value;
break;
case "summary":
manifestMetadata.Summary = value;
break;
case "releaseNotes":
manifestMetadata.ReleaseNotes = value;
break;
case "copyright":
manifestMetadata.Copyright = value;
break;
case "language":
manifestMetadata.Language = value;
break;
case "title":
manifestMetadata.Title = value;
break;
case "tags":
manifestMetadata.Tags = value;
break;
case "dependencies":
manifestMetadata.DependencySets = ReadDependencySets(element);
break;
case "frameworkAssemblies":
manifestMetadata.FrameworkAssemblies = ReadFrameworkAssemblies(element);
break;
case "references":
manifestMetadata.PackageAssemblyReferences = ReadReferenceSets(element);
break;
case "contentFiles":
manifestMetadata.ContentFiles = ReadContentFiles(element);
break;
}
}
private static List<ManifestContentFiles> ReadContentFiles(XElement contentFilesElement)
{
if (!contentFilesElement.HasElements)
{
return new List<ManifestContentFiles>(0);
}
var contentFileSets = (from element in contentFilesElement.ElementsNoNamespace("files")
let includeAttribute = element.Attribute("include")
where includeAttribute != null && !string.IsNullOrEmpty(includeAttribute.Value)
let excludeAttribute = element.Attribute("exclude")
let buildActionAttribute = element.Attribute("buildAction")
let copyToOutputAttribute = element.Attribute("copyToOutput")
let flattenAttribute = element.Attribute("flatten")
select new ManifestContentFiles
{
Include = includeAttribute.Value?.Trim(),
Exclude = excludeAttribute == null ? null : excludeAttribute.Value,
BuildAction = buildActionAttribute == null ? null : buildActionAttribute.Value,
CopyToOutput = copyToOutputAttribute == null ? null : copyToOutputAttribute.Value,
Flatten = flattenAttribute == null ? null : flattenAttribute.Value
}).ToList();
return contentFileSets;
}
private static List<PackageReferenceSet> ReadReferenceSets(XElement referencesElement)
{
if (!referencesElement.HasElements)
{
return new List<PackageReferenceSet>(0);
}
if (referencesElement.ElementsNoNamespace("group").Any() &&
referencesElement.ElementsNoNamespace("reference").Any())
{
// TODO: Resources
throw new InvalidDataException("NuGetResources.Manifest_ReferencesHasMixedElements");
}
var references = ReadReference(referencesElement, throwIfEmpty: false);
if (references.Count > 0)
{
// old format, <reference> is direct child of <references>
var referenceSet = new PackageReferenceSet(references);
return new List<PackageReferenceSet> { referenceSet };
}
else
{
var groups = referencesElement.ElementsNoNamespace("group");
return (from element in groups
select new PackageReferenceSet(element.GetOptionalAttributeValue("targetFramework")?.Trim(),
ReadReference(element, throwIfEmpty: true))).ToList();
}
}
public static List<string> ReadReference(XElement referenceElement, bool throwIfEmpty)
{
var references = (from element in referenceElement.ElementsNoNamespace("reference")
let fileAttribute = element.Attribute("file")
where fileAttribute != null && !String.IsNullOrEmpty(fileAttribute.Value)
select fileAttribute.Value?.Trim()
).ToList();
if (throwIfEmpty && references.Count == 0)
{
// TODO: Resources
throw new InvalidDataException("NuGetResources.Manifest_ReferencesIsEmpty");
}
return references;
}
private static List<FrameworkAssemblyReference> ReadFrameworkAssemblies(XElement frameworkElement)
{
if (!frameworkElement.HasElements)
{
return new List<FrameworkAssemblyReference>(0);
}
return (from element in frameworkElement.ElementsNoNamespace("frameworkAssembly")
let assemblyNameAttribute = element.Attribute("assemblyName")
where assemblyNameAttribute != null && !String.IsNullOrEmpty(assemblyNameAttribute.Value)
select new FrameworkAssemblyReference(assemblyNameAttribute.Value?.Trim(),
new[] { NuGetFramework.Parse(element.GetOptionalAttributeValue("targetFramework")?.Trim()) })
).ToList();
}
private static List<PackageDependencySet> ReadDependencySets(XElement dependenciesElement)
{
if (!dependenciesElement.HasElements)
{
return new List<PackageDependencySet>();
}
// Disallow the <dependencies> element to contain both <dependency> and
// <group> child elements. Unfortunately, this cannot be enforced by XSD.
if (dependenciesElement.ElementsNoNamespace("dependency").Any() &&
dependenciesElement.ElementsNoNamespace("group").Any())
{
// TODO: Resources
throw new InvalidDataException("NuGetResources.Manifest_DependenciesHasMixedElements");
}
var dependencies = ReadDependencies(dependenciesElement);
if (dependencies.Count > 0)
{
// old format, <dependency> is direct child of <dependencies>
var dependencySet = new PackageDependencySet(dependencies);
return new List<PackageDependencySet> { dependencySet };
}
else
{
var groups = dependenciesElement.ElementsNoNamespace("group");
return (from element in groups
select new PackageDependencySet(element.GetOptionalAttributeValue("targetFramework")?.Trim(),
ReadDependencies(element))).ToList();
}
}
private static List<PackageDependency> ReadDependencies(XElement containerElement)
{
// element is <dependency>
return (from element in containerElement.ElementsNoNamespace("dependency")
let idElement = element.Attribute("id")
where idElement != null && !String.IsNullOrEmpty(idElement.Value)
select new PackageDependency(
idElement.Value?.Trim(),
VersionRange.Parse(element.GetOptionalAttributeValue("version")?.Trim()),
element.GetOptionalAttributeValue("include")?.Trim()?.Split(',').Select(a => a.Trim()).ToArray(),
element.GetOptionalAttributeValue("exclude")?.Trim()?.Split(',').Select(a => a.Trim()).ToArray()
)).ToList();
}
private static List<ManifestFile> ReadFilesList(XElement xElement)
{
if (xElement == null)
{
return null;
}
List<ManifestFile> files = new List<ManifestFile>();
foreach (var file in xElement.ElementsNoNamespace("file"))
{
var srcElement = file.Attribute("src");
if (srcElement == null || String.IsNullOrEmpty(srcElement.Value))
{
continue;
}
string target = file.GetOptionalAttributeValue("target")?.Trim();
string exclude = file.GetOptionalAttributeValue("exclude")?.Trim();
// Multiple sources can be specified by using semi-colon separated values.
files.AddRange(from source in srcElement.Value.Trim(';').Split(';')
select new ManifestFile(source?.Trim(), target?.Trim(), exclude?.Trim() ));
}
return files;
}
}
}

View file

@ -75,80 +75,50 @@ namespace NuGet
return elem;
}
public static Manifest ReadManifest(this XElement element, XNamespace ns)
{
if (element.Name != ns + "package")
{
return null;
}
var metadataElement = element.Element(ns + "metadata");
if (metadataElement == null)
{
return null;
}
ManifestMetadata metadata = new ManifestMetadata();
metadata.MinClientVersionString = metadataElement.Attribute("minClientVersion")?.Value;
metadata.Id = metadataElement.Element(ns + "id")?.Value;
metadata.Version = ConvertIfNotNull(metadataElement.Element(ns + "version")?.Value, s => new NuGetVersion(s));
metadata.Title = metadataElement.Element(ns + "title")?.Value;
metadata.RequireLicenseAcceptance = ConvertIfNotNull(metadataElement.Element(ns + "requireLicenseAcceptance")?.Value, s => bool.Parse(s));
metadata.DevelopmentDependency = ConvertIfNotNull(metadataElement.Element(ns + "developmentDependency")?.Value, s => bool.Parse(s));
metadata.Authors = ConvertIfNotNull(metadataElement.Element(ns + "authors")?.Value, s => s.Split(','));
metadata.Owners = ConvertIfNotNull(metadataElement.Element(ns + "owners")?.Value, s => s.Split(','));
metadata.LicenseUrl = ConvertIfNotNull(metadataElement.Element(ns + "licenseUrl")?.Value, s => new Uri(s));
metadata.ProjectUrl = ConvertIfNotNull(metadataElement.Element(ns + "projectUrl")?.Value, s => new Uri(s));
metadata.IconUrl = ConvertIfNotNull(metadataElement.Element(ns + "iconUrl")?.Value, s => new Uri(s));
metadata.Description = metadataElement.Element(ns + "description")?.Value;
metadata.Summary = metadataElement.Element(ns + "summary")?.Value;
metadata.ReleaseNotes = metadataElement.Element(ns + "releaseNotes")?.Value;
metadata.Copyright = metadataElement.Element(ns + "copyright")?.Value;
metadata.Language = metadataElement.Element(ns + "language")?.Value;
metadata.Tags = metadataElement.Element(ns + "tags")?.Value;
metadata.DependencySets = GetItemSetsFromGroupableXElements(
ns,
metadataElement,
Dependencies,
"dependency",
TargetFramework,
GetPackageDependencyFromXElement,
(tfm, deps) => new PackageDependencySet(tfm, deps));
metadata.PackageAssemblyReferences = GetItemSetsFromGroupableXElements(
ns,
metadataElement,
References,
Reference,
TargetFramework,
GetPackageReferenceFromXElement,
(tfm, refs) => new PackageReferenceSet(tfm, refs)).ToArray();
metadata.FrameworkAssemblies = GetFrameworkAssembliesFromXElement(ns, metadataElement);
metadata.ContentFiles = GetManifestContentFilesFromXElement(ns, metadataElement);
Manifest manifest = new Manifest(metadata);
var files = GetManifestFilesFromXElement(ns, element);
if (files != null)
{
foreach(var file in files)
{
manifest.Files.Add(file);
}
}
return manifest;
}
public static XElement ToXElement(this IEnumerable<ManifestFile> fileList, XNamespace ns)
{
return GetXElementFromManifestFiles(ns, fileList);
}
public static string GetOptionalAttributeValue(this XElement element, string localName, string namespaceName = null)
{
XAttribute attr;
if (string.IsNullOrEmpty(namespaceName))
{
attr = element.Attribute(localName);
}
else
{
attr = element.Attribute(XName.Get(localName, namespaceName));
}
return attr != null ? attr.Value : null;
}
public static string GetOptionalElementValue(this XContainer element, string localName, string namespaceName = null)
{
XElement child;
if (string.IsNullOrEmpty(namespaceName))
{
child = element.ElementsNoNamespace(localName).FirstOrDefault();
}
else
{
child = element.Element(XName.Get(localName, namespaceName));
}
return child != null ? child.Value : null;
}
public static IEnumerable<XElement> ElementsNoNamespace(this XContainer container, string localName)
{
return container.Elements().Where(e => e.Name.LocalName == localName);
}
public static IEnumerable<XElement> ElementsNoNamespace(this IEnumerable<XContainer> source, string localName)
{
return source.Elements().Where(e => e.Name.LocalName == localName);
}
private static XElement GetXElementFromGroupableItemSets<TSet, TItem>(
XNamespace ns,
IEnumerable<TSet> objectSets,
@ -206,45 +176,11 @@ namespace NuGet
return new XElement(ns + parentName, childElements.ToArray());
}
private static IEnumerable<TSet> GetItemSetsFromGroupableXElements<TSet, TItem>(
XNamespace ns,
XElement parent,
string rootName,
string elementName,
string identifierAttributeName,
Func<XElement, TItem> getItemFromXElement,
Func<string, IEnumerable<TItem>, TSet> getItemSet)
{
XElement rootElement = parent.Element(ns + rootName);
if (rootElement == null)
{
return Enumerable.Empty<TSet>();
}
var groups = rootElement.Elements(ns + Group);
if (groups == null || !groups.Any())
{
// no groupable sets, all are ungroupable
return new[] { getItemSet(null, rootElement.Elements(ns + elementName).Select(e => getItemFromXElement(e))) };
}
return groups.Select(g =>
getItemSet(g.Attribute(identifierAttributeName)?.Value,
g.Elements(ns + elementName).Select(e => getItemFromXElement(e))));
}
private static XElement GetXElementFromPackageReference(XNamespace ns, string reference)
{
return new XElement(ns + Reference, new XAttribute(File, reference));
}
private static string GetPackageReferenceFromXElement(XElement element)
{
return element.Attribute(File)?.Value;
}
private static XElement GetXElementFromPackageDependency(XNamespace ns, PackageDependency dependency)
{
return new XElement(ns + "dependency",
@ -254,14 +190,6 @@ namespace NuGet
dependency.Exclude != null && dependency.Exclude.Any() ? new XAttribute("exclude", string.Join(",", dependency.Exclude)) : null);
}
private static PackageDependency GetPackageDependencyFromXElement(XElement element)
{
return new PackageDependency(element.Attribute("id").Value,
ConvertIfNotNull(element.Attribute("version")?.Value, s => VersionRange.Parse(s)),
ConvertIfNotNull(element.Attribute("include")?.Value, s => s.Split(',')),
ConvertIfNotNull(element.Attribute("exclude")?.Value, s => s.Split(',')));
}
private static XElement GetXElementFromFrameworkAssemblies(XNamespace ns, IEnumerable<FrameworkAssemblyReference> references)
{
if (references == null || !references.Any())
@ -279,22 +207,6 @@ namespace NuGet
null)));
}
private static IEnumerable<FrameworkAssemblyReference> GetFrameworkAssembliesFromXElement(XNamespace ns, XElement parent)
{
var frameworkAssembliesElement = parent.Element(ns + FrameworkAssemblies);
if (frameworkAssembliesElement == null)
{
return null;
}
return frameworkAssembliesElement.Elements(ns + FrameworkAssembly).Select(e =>
new FrameworkAssemblyReference(e.Attribute(AssemblyName).Value,
e.Attribute("targetFramework")?.Value?.Split(',')?
.Select(tf => NuGetFramework.Parse(tf)) ?? Enumerable.Empty<NuGetFramework>()));
}
private static XElement GetXElementFromManifestFiles(XNamespace ns, IEnumerable<ManifestFile> files)
{
if (files == null || !files.Any())
@ -311,21 +223,6 @@ namespace NuGet
)));
}
private static IEnumerable<ManifestFile> GetManifestFilesFromXElement(XNamespace ns, XElement parent)
{
var filesElement = parent.Element(ns + Files);
if (filesElement == null)
{
return null;
}
return filesElement.Elements(ns + File).Select(f =>
new ManifestFile(f.Attribute("src").Value,
f.Attribute("target").Value,
f.Attribute("exclude")?.Value));
}
private static XElement GetXElementFromManifestContentFiles(XNamespace ns, IEnumerable<ManifestContentFiles> contentFiles)
{
if (contentFiles == null || !contentFiles.Any())
@ -344,26 +241,6 @@ namespace NuGet
)));
}
private static ICollection<ManifestContentFiles> GetManifestContentFilesFromXElement(XNamespace ns, XElement parent)
{
var contentFilesElement = parent.Element(ns + "contentFiles");
if (contentFilesElement == null)
{
return null;
}
return contentFilesElement.Elements(ns + File).Select(cf =>
new ManifestContentFiles()
{
Include = cf.Attribute("include")?.Value,
Exclude = cf.Attribute("exclude")?.Value,
BuildAction = cf.Attribute("buildAction")?.Value,
CopyToOutput = cf.Attribute("copyToOutput")?.Value,
Flatten = cf.Attribute("flatten")?.Value,
}).ToArray();
}
private static void AddElementIfNotNull<T>(XElement parent, XNamespace ns, string name, T value)
where T : class
{
@ -385,16 +262,5 @@ namespace NuGet
}
}
}
private static TDest ConvertIfNotNull<TDest, TSource>(TSource value, Func<TSource, TDest> convert)
{
if (value != null)
{
var converted = convert(value);
return converted;
}
return default(TDest);
}
}
}