Skip to content

Commit

Permalink
adding the ability to de-serialize PartAtom of Revit files
Browse files Browse the repository at this point in the history
  • Loading branch information
salaros committed Apr 30, 2020
1 parent f2f2325 commit ab40a02
Show file tree
Hide file tree
Showing 20 changed files with 677 additions and 105 deletions.
4 changes: 1 addition & 3 deletions src/Decompiled/DisplayUnitType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1320,7 +1320,7 @@ public static bool TryGetCatalogString(this DisplayUnitType displayUnitType, out
/// <returns></returns>
public static bool TryGetFromCatalogString(this string catalogString, out DisplayUnitType displayUnitType)
{
var values = _dutToCatalog?.Where(u => u.Value.Equals(catalogString)).Select(x => x.Key).ToList();
var values = _dutToCatalog?.Where(u => u.Value.Equals(catalogString, StringComparison.OrdinalIgnoreCase)).Select(x => x.Key).ToList();
var valueExists = values != null && values.Any();
displayUnitType = valueExists
? values.FirstOrDefault()
Expand Down Expand Up @@ -1400,7 +1400,5 @@ public static bool TryGetFromUnitSymbol(this UnitSymbolType unitSymbolType, out
: DisplayUnitType.DUT_UNDEFINED;
return values.Any();
}


}
}
3 changes: 2 additions & 1 deletion src/Decompiled/ParameterType.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;

Expand Down Expand Up @@ -449,7 +450,7 @@ static ParameterTypeExtensions()
/// <returns></returns>
public static ParameterType FromSharedDataType(this string dataType)
{
var values = _parameterTypes2Shared?.Where(u => u.Value.Equals(dataType)).Select(x => x.Key).ToArray();
var values = _parameterTypes2Shared?.Where(u => u.Value.Equals(dataType, StringComparison.OrdinalIgnoreCase)).Select(x => x.Key).ToArray();
return (values != null && values.Any())
? values.FirstOrDefault()
: ParameterType.Invalid;
Expand Down
2 changes: 1 addition & 1 deletion src/Decompiled/UnitType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ public static bool TryGetGroup(this UnitType unitType, out UnitGroup group)
/// <returns></returns>
public static bool TryGetUnitTypeFromCatalogString(this string catalogString, out UnitType unitType)
{
var values = _utToCatalog?.Where(u => u.Value.Equals(catalogString)).Select(x => x.Key).ToList();
var values = _utToCatalog?.Where(u => u.Value.Equals(catalogString, System.StringComparison.OrdinalIgnoreCase)).Select(x => x.Key).ToList();
var valueExists = values != null && values.Any();
unitType = valueExists
? values.FirstOrDefault()
Expand Down
130 changes: 130 additions & 0 deletions src/Extensions/RevitFileInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

namespace CodeCave.Revit.Toolkit
{
public static class RevitFileInfoExtensions
{
#region Constants

/// <summary>
/// List of known Revit file properties.
/// </summary>
internal struct KnownRevitInfoProps
{
public const string USERNAME = "Username";
public const string CENTRAL_MODEL_PATH = "Central Model Path";
public const string REVIT_BUILD = "Revit Build";
public const string LAST_SAVE_PATH = "Last Save Path";
public const string OPEN_WORKSET_DEFAULT = "Open Workset Default";
public const string PROJECT_SPARK_FILE = "Project Spark File";
public const string CENTRAL_MODEL_IDENTITY = "Central Model Identity";
public const string LOCALE = "Locale when saved";
public const string LOCAL_SAVED_TO_CENTRAL = "All Local Changes Saved To Central";
public const string CENTRAL_MODEL_VERSION = "Central model's version number corresponding to the last reload latest";
public const string CENTRAL_MODEL_GUID = "Central model's episode GUID corresponding to the last reload latest";
public const string UNIQUE_DOCUMENT_GUID = "Unique Document GUID";
public const string UNIQUE_DOCUMENT_INCREMENT = "Unique Document Increments";
}

private static readonly Regex versionExtractor =
new Regex(@"(?<vendor>\w*) (?<software>(\w|\s)*) (?<version>\d{4}) \(Build\: (?<build>\d*)_(?<revision>\d*)(\((?<arch>\w*)\))?\)",
RegexOptions.Compiled |
RegexOptions.IgnoreCase |
RegexOptions.CultureInvariant);

#endregion

#region Methods

/// <summary>
/// Parses the username.
/// </summary>
/// <param name="revitFileInfo">The revit file information.</param>
/// <param name="properties">The properties.</param>
/// <exception cref="T:System.ArgumentNullException">properties</exception>
public static void ParseUsername(this RevitFileInfo revitFileInfo, Dictionary<string, string> properties)
{
if (properties == null)
throw new ArgumentNullException(nameof(properties));

// Parse username field
if (!properties.ContainsKey(KnownRevitInfoProps.USERNAME))
return;

revitFileInfo.Username = properties[KnownRevitInfoProps.USERNAME];
}

/// <summary>
/// Parses the revit.
/// </summary>
/// <param name="revitFileInfo">The revit file information.</param>
/// <param name="properties">The properties.</param>
/// <exception cref="T:System.ArgumentNullException">properties</exception>
public static void ParseRevit(this RevitFileInfo revitFileInfo, Dictionary<string, string> properties)
{
if (properties == null) throw new ArgumentNullException(nameof(properties));

// Parse Revit Build string
if (!properties.ContainsKey(KnownRevitInfoProps.REVIT_BUILD))
return;

var versionObj = versionExtractor.Match(properties[KnownRevitInfoProps.REVIT_BUILD]);
if (!versionObj.Success)
return;

revitFileInfo.Vendor = versionObj?.Groups["vendor"]?.Value;
revitFileInfo.Name = versionObj?.Groups["software"]?.Value;
revitFileInfo.Is64Bit = versionObj.Groups["arch"]?.Value?.Contains("x64") ?? false;

var versionString = string.Format("{0}.{1}.{2}",
versionObj.Groups["version"].Value,
versionObj.Groups["build"].Value,
versionObj.Groups["revision"].Value);
revitFileInfo.Version = new Version(versionString);
}

/// <summary>
/// Parses the locale.
/// </summary>
/// <param name="revitFileInfo">The revit file information.</param>
/// <param name="properties">The properties.</param>
/// <exception cref="T:System.ArgumentNullException">properties</exception>
public static void ParseLocale(this RevitFileInfo revitFileInfo, Dictionary<string, string> properties)
{
if (properties == null) throw new ArgumentNullException(nameof(properties));

// Parse last saved locale
if (!properties.ContainsKey(KnownRevitInfoProps.LOCALE))
return;

var localeRaw = properties[KnownRevitInfoProps.LOCALE];
revitFileInfo.Locale = CultureInfo
.GetCultures(CultureTypes.NeutralCultures)
.FirstOrDefault(c => c.ThreeLetterWindowsLanguageName == localeRaw);
}

/// <summary>
/// Parses the document information.
/// </summary>
/// <param name="revitFileInfo">The revit file information.</param>
/// <param name="properties">The properties.</param>
/// <exception cref="T:System.ArgumentNullException">properties</exception>
public static void ParseDocumentInfo(this RevitFileInfo revitFileInfo, Dictionary<string, string> properties)
{
if (properties == null) throw new ArgumentNullException(nameof(properties));

// Parse document unique id
if (!properties.ContainsKey(KnownRevitInfoProps.UNIQUE_DOCUMENT_GUID))
return;

var guidRaw = properties[KnownRevitInfoProps.UNIQUE_DOCUMENT_GUID];
revitFileInfo.Guid = new Guid(guidRaw);
}

#endregion
}
}
12 changes: 4 additions & 8 deletions src/OLE/OleDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace CodeCave.Revit.Toolkit.OLE
/// <summary>
/// A reader for OLE metadata information stored in Revit files
/// </summary>
public class OleDataReader
public static class OleDataReader
{
/// <summary>
/// Gets the raw XML data.
Expand Down Expand Up @@ -167,13 +167,9 @@ public static TResult GetData<TResult>(string pathToFile, string oleStream, Enco
{
var stringData = GetRawString(pathToFile, oleStream, enc);
var xmlSerializer = new XmlSerializer(typeof(TResult));
using (var stringReader = new StringReader(stringData))
{
using (var xmlReader = XmlReader.Create(stringReader))
{
resultObject = xmlSerializer.Deserialize(xmlReader) as TResult;
}
}
using var stringReader = new StringReader(stringData);
using var xmlReader = XmlReader.Create(stringReader);
resultObject = xmlSerializer.Deserialize(xmlReader) as TResult;
}
catch (Exception ex)
{
Expand Down
168 changes: 168 additions & 0 deletions src/OLE/RevitFileInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using CodeCave.Revit.Toolkit.OLE;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

namespace CodeCave.Revit.Toolkit
{
public class RevitFileInfo
{
#region Properties

/// <summary>
/// Gets the username.
/// </summary>
/// <value>The username.</value>
public string Username { get; internal set; }

/// <summary>
/// Gets the unique identifier.
/// </summary>
/// <value>
/// The unique identifier.
/// </value>
public Guid Guid { get; internal set; }

/// <summary>
/// Gets the locale.
/// </summary>
/// <value>
/// The locale.
/// </value>
public CultureInfo Locale { get; internal set; }

/// <summary>
/// Gets or sets the part atom.
/// </summary>
/// <value>
/// The part atom.
/// </value>
public PartAtom PartAtom { get; private set; }

/// <summary>
/// Gets the category.
/// </summary>
/// <value>
/// The category.
/// </value>
public string Category => PartAtom?.Category;

/// <summary>
/// Gets the types.
/// </summary>
/// <value>
/// The types.
/// </value>
public FamilyType[] Types => PartAtom?.Family?.Parts;

public Version Version { get; internal set; }

public string Name { get; internal set; }

public string Vendor { get; internal set; }

public object Architecture { get; internal set; }

public bool Is64Bit { get; internal set; }

public string FilePath { get; private set; }

#endregion

#region Methods

/// <summary>
/// Gets a <see cref="RevitFileInfo" /> instance from the given file path.
/// </summary>
/// <param name="filePath">The file path.</param>
/// <param name="readProperties">if set to <c>true</c> [read properties].</param>
/// <param name="readTypes">if set to <c>true</c> [read types].</param>
/// <returns></returns>
/// <exception cref="T:System.ArgumentException">filePath is invalid</exception>
public static RevitFileInfo GetFromFile(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath))
throw new ArgumentException("filePath is invalid");

var rfi = new RevitFileInfo
{
FilePath = filePath
};

// Read basic file info from metadata
try
{
var properties = GetProperties(filePath);
rfi.ParseDocumentInfo(properties);
rfi.ParseLocale(properties);
rfi.ParseRevit(properties);
rfi.ParseUsername(properties);
}
catch (Exception)
{
// TODO log the error
}

// Read family types from part atom
try
{
rfi.PartAtom = PartAtom.GetFromFile(filePath);
}
catch (Exception)
{
// TODO log the error
}

return rfi;
}

#endregion

#region Helpers

/// <summary>
/// Gets the properties.
/// </summary>
/// <param name="filePath">The file path.</param>
/// <returns></returns>
internal static Dictionary<string, string> GetProperties(string filePath)
{
var basicInfo = OleDataReader.GetRawBytes(filePath, "BasicFileInfo");
var fileProps = new Dictionary<string, string>();
var supportedEncodeings = new[]
{
Encoding.Unicode,
Encoding.BigEndianUnicode,
Encoding.UTF8,
Encoding.ASCII,
Encoding.Default,
};

foreach (var encoding in supportedEncodeings)
{
var basicInfoString = encoding.GetString(basicInfo)?.TrimEnd('\u0a0d');
using var basicInfoReader = new StringReader(basicInfoString);

// ReSharper disable once RedundantAssignment
var stringLine = basicInfoReader.ReadLine(); // skip the first line
while (!string.IsNullOrWhiteSpace(stringLine = basicInfoReader.ReadLine()))
{
var parts = stringLine.Split(new[] { ":" }, 2, StringSplitOptions.None);
if (parts.Length != 2)
continue;
fileProps.Add(parts[0].Trim(), parts[1].Trim());
}

if (fileProps.Any())
break;
}

return fileProps;
}

#endregion
}
}
Loading

0 comments on commit ab40a02

Please sign in to comment.