|
//-----------------------------------------------------------------------------
//
// <copyright file="CompoundFileReference.cs" company="Microsoft">
// Copyright (C) Microsoft Corporation. All rights reserved.
// </copyright>
//
// Description:
// Implementation of the CompoundFileReference base class.
//
// History:
// 03/31/2003: BruceMac: Created (from ContainerReference.cs)
// 04/08/2003: BruceMac: Made into abstract base class.
// 05/20/2003: RogerCh: Ported to WCP tree. Split CompoundFileSubStreamReference.cs
// (2 classes) into individual files for ByteRange and Index
// reference.
// 08/11/2003: LGolding: Fix Bug 864168 (some of BruceMac's bug fixes were lost
// in port to WCP tree).
//
// Notes:
// Persistence of specific classes is mostly hard-coded in this base class because
// the persistence must follow a shared binary implementation with Office. It is
// also intentionally not polymorphic because we don't allow arbitrary subclasses
// to participate.
//-----------------------------------------------------------------------------
using System;
using System.Collections.Specialized; // for StringCollection class
using System.IO;
using System.Diagnostics; // for Debug.Assert
using System.Windows; // for SR error message lookup
using MS.Internal.WindowsBase;
namespace MS.Internal.IO.Packaging.CompoundFile
{
/// <summary>
/// Logical reference to a portion of a container
/// </summary>
/// <remarks>
/// Use this class to represent a logical reference to a portion of a container such as a stream
/// Note that a CompoundFileReference is not natively tied to any specific container. This lack of context allows
/// the developer freedom to create the reference in the absence of the container, or to have the reference
/// refer to any one of multiple containers having a similar format.
/// </remarks>
internal abstract class CompoundFileReference: IComparable
{
#region Enums
/// <summary>
/// Reference component types
/// </summary>
/// <remarks>
/// These are only used for serialization
/// </remarks>
private enum RefComponentType : int
{
/// <summary>
/// Stream component
/// </summary>
Stream = 0,
/// <summary>
/// Storage component
/// </summary>
Storage = 1,
};
#endregion
#region Abstracts
/// <summary>
/// Full name of the stream or storage this reference refers to (see StreamInfo and StorageInfo)
/// </summary>
abstract public string FullName {get;}
#endregion
#region IComparable
/// <summary>
/// This is not implemented - it exists as a reminder to authors of subclasses that they must implement this interface
/// </summary>
/// <param name="ob">ignored</param>
int IComparable.CompareTo(object ob)
{
// this must be implemented by our inheritors
Debug.Assert(false, "subclasses must override this method");
return 0;
}
#endregion
#region Operators
/// <summary>Compare for equality</summary>
/// <param name="o">the CompoundFileReference to compare to</param>
public override bool Equals(object o)
{
// this must be implemented by our inheritors
Debug.Assert(false, "subclasses must override this method");
return false;
}
/// <summary>Returns an integer suitable for including this object in a hash table</summary>
public override int GetHashCode()
{
// this must be implemented by our inheritors
Debug.Assert(false, "subclasses must override this method");
return 0;
}
#endregion
#region Persistence
/// <summary>Save to a stream</summary>
/// <param name="reference">reference to save</param>
/// <param name="writer">The BinaryWriter to persist this object to.
/// This method will alter the stream pointer of the underlying stream as a side effect.
/// Passing null simply calculates how many bytes would be written.</param>
/// <returns>number of bytes written including any padding</returns>
static internal int Save(CompoundFileReference reference, BinaryWriter writer)
{
int bytes = 0;
// NOTE: Our RefComponentType must be written by our caller
bool calcOnly = (writer == null);
// what are we dealing with here?
CompoundFileStreamReference streamReference = reference as CompoundFileStreamReference;
if ((streamReference == null) && (!(reference is CompoundFileStorageReference)))
throw new ArgumentException(SR.Get(SRID.UnknownReferenceSerialize), "reference");
// first parse the path into strings
string[] segments = ContainerUtilities.ConvertBackSlashPathToStringArrayPath(reference.FullName);
int entries = segments.Length;
// write the count
if (!calcOnly)
writer.Write( entries );
bytes += ContainerUtilities.Int32Size;
// write the segments - if we are dealing with a stream entry, don't write the last "segment"
// because it is in fact a stream name
for (int i = 0; i < segments.Length - (streamReference == null ? 0 : 1); i++)
{
if (!calcOnly)
{
writer.Write( (Int32)RefComponentType.Storage );
}
bytes += ContainerUtilities.Int32Size;
bytes += ContainerUtilities.WriteByteLengthPrefixedDWordPaddedUnicodeString(writer, segments[i]);
}
if (streamReference != null)
{
// we are responsible for the prefix
if (!calcOnly)
{
writer.Write( (Int32)RefComponentType.Stream );
}
bytes += ContainerUtilities.Int32Size;
// write the stream name
bytes += ContainerUtilities.WriteByteLengthPrefixedDWordPaddedUnicodeString(writer, segments[segments.Length - 1]);
}
return bytes;
}
/// <summary>
/// Deserialize from the given stream
/// </summary>
/// <param name="reader">the BinaryReader to deserialize from with the seek pointer at the beginning of the container reference</param>
/// <param name="bytesRead">bytes consumed from the stream</param>
/// <remarks>
/// Side effect of change the stream pointer
/// </remarks>
/// <exception cref="FileFormatException">Throws a FileFormatException if any formatting errors are encountered</exception>
internal static CompoundFileReference Load(BinaryReader reader, out int bytesRead)
{
ContainerUtilities.CheckAgainstNull( reader, "reader" );
bytesRead = 0; // running count of how much we've read - sanity check
// create the TypeMap
// reconstitute ourselves from the given BinaryReader
// in this version, the next Int32 is the number of entries
Int32 entryCount = reader.ReadInt32();
bytesRead += ContainerUtilities.Int32Size;
// EntryCount of zero indicates the root storage.
if (entryCount < 0)
throw new FileFormatException(
SR.Get(SRID.CFRCorrupt));
// need a temp collection because we don't know what we're dealing with until a non-storage component
// type is encountered
StringCollection storageList = null;
String streamName = null;
// loop through the entries - accumulating strings until we know what kind of object
// we ultimately need
int byteLength; // reusable
while (entryCount > 0)
{
// first Int32 tells us what kind of component this entry represents
RefComponentType refType = (RefComponentType)reader.ReadInt32();
bytesRead += ContainerUtilities.Int32Size;
switch (refType)
{
case RefComponentType.Storage:
{
if (streamName != null)
throw new FileFormatException(
SR.Get(SRID.CFRCorruptStgFollowStm));
if (storageList == null)
storageList = new StringCollection();
String str = ContainerUtilities.ReadByteLengthPrefixedDWordPaddedUnicodeString(reader, out byteLength);
bytesRead += byteLength;
storageList.Add(str);
} break;
case RefComponentType.Stream:
{
if (streamName != null)
throw new FileFormatException(
SR.Get(SRID.CFRCorruptMultiStream));
streamName = ContainerUtilities.ReadByteLengthPrefixedDWordPaddedUnicodeString(reader, out byteLength);
bytesRead += byteLength;
} break;
// we don't handle these types yet
default:
throw new FileFormatException(
SR.Get(SRID.UnknownReferenceComponentType));
}
--entryCount;
}
CompoundFileReference newRef = null;
// stream or storage?
if (streamName == null)
{
newRef = new CompoundFileStorageReference(
ContainerUtilities.ConvertStringArrayPathToBackSlashPath(storageList));
}
else
newRef = new CompoundFileStreamReference(
ContainerUtilities.ConvertStringArrayPathToBackSlashPath(storageList, streamName));
return newRef;
}
#endregion
}
}
|