I'm developing an MP4 parser. Each class represents each box in MP4 file, and every box class derives from base box class. The problem is that below code is duplicated on every box class:
private ulong MyDataSize;
public override ulong BoxContentEndOffset
{
get { return base.BoxContentEndOffset + this.MyDataSize; }
}
protected new ParsingState MyParsingState { get; set; }
public override bool IsParsingDone
{
get { return this.MyParsingState.IsParsingDone(); }
}
public override bool CanContinueParsing
{
get { return ((base.MyParsingState.IsParsingDone() ||
base.MyParsingState.CanContinueParsing()) && this.MyParsingState.CanContinueParsing()); }
}
Also, each box-container class has duplicated code like below. (AsReadOnly is an extension method here)
public sealed class SampleTableBox : SimpleContainerBoxBase, IActualElement<IBox>
{
private static readonly IElementType<IBox>[] _ImplementedElementTypes
= { BoxElementType.Get("stbl") };
public ICollection<IElementType<IBox>> ImplementedElementTypes
{
get { return _ImplementedElementTypes; }
}
private static readonly ReadOnlyCollection<IElementType> _PossibleParents
= new ReadOnlyCollection<IElementType>(
new IElementType[]
{
BoxElementType.Get("minf")
});
public override ICollection<IElementType> PossibleParents
{
get { return _PossibleParents; }
}
private static ReadOnlyDictionary<ICollection<IElementType>, Tuple<int, int>> _RequiredElements
= new ReadOnlyDictionary<ICollection<IElementType>, Tuple<int, int>>(
new Dictionary<ICollection<IElementType>, Tuple<int, int>>()
{
{ new [] { BoxElementType.Get("stsd") }.AsReadOnly<IElementType>(), new Tuple<int, int>(1, 1) },
{ new [] { BoxElementType.Get("stts") }.AsReadOnly<IElementType>(), new Tuple<int, int>(1, 1) },
{ new [] { BoxElementType.Get("stsz"), BoxElementType.Get("stz2") }.AsReadOnly<IElementType>(), new Tuple<int, int>(1, 1) },
{ new [] { BoxElementType.Get("stsc") }.AsReadOnly<IElementType>(), new Tuple<int, int>(1, 1) },
{ new [] { BoxElementType.Get("stco"), BoxElementType.Get("co64") }.AsReadOnly<IElementType>(), new Tuple<int, int>(1, 1) }
});
public override IDictionary<ICollection<IElementType>, Tuple<int, int>> RequiredElements
{
get { return _RequiredElements; }
}
}
What is the best way to reduce duplicate code?
EDIT: SampleContainerBoxBase code
public abstract class SimpleContainerBoxBase : BoxBase, IParent<IBox>
{
private ElementArray<IBox> _BoxArray;
private ulong MyDataSize;
public override ulong BoxContentEndOffset
{
get { return base.BoxContentEndOffset + this.MyDataSize; }
}
protected new ParsingState MyParsingState { get; private set; }
public override bool IsParsingDone
{
get { return this.MyParsingState.IsParsingDone(); }
}
public override bool CanContinueParsing
{
get { return ((base.MyParsingState.IsParsingDone() || base.MyParsingState.CanContinueParsing()) && this.MyParsingState.CanContinueParsing()); }
}
public IElementType ParentType
{
get { return this.BoxType; }
}
public virtual IDictionary<ICollection<IElementType>, Tuple<int, int>> RequiredElements
{
get { return null; }
}
public virtual bool CheckChildAdditionalRequirements()
{
return this.CallCheckChildAdditionalRequirements();
}
public ICollection<IBox> ChildElements
{
get { return (this._BoxArray == null ? null : _BoxArray.Elements); }
}
public override MP4ParsingError TryParse(Stream stream, IParent parent, ulong boxPosition, ulong boxLength)
{
MP4ParsingError error;
if (!base.MyParsingState.IsParsingDone())
{
error = base.TryParse(stream, parent, boxPosition, boxLength);
if (error != MP4ParsingError.Success)
return error;
}
if (this.MyParsingState == ParsingState.Initial)
{
this._BoxArray = new ElementArray<IBox>();
ulong availableDataLength = this.Size - base.BoxContentEndOffset;
this.MyDataSize = availableDataLength;
this.MyParsingState = ParsingState.ParsedPartially;
}
error = _BoxArray.TryParse(stream, this, boxPosition + base.BoxContentEndOffset, this.MyDataSize);
if (error == MP4ParsingError.Success)
{
/* success */
}
else if (error == MP4ParsingError.DelayedParsingRequired)
{
this.MyParsingState = ParsingState.DelayedParsing;
return MP4ParsingError.DelayedParsingRequired;
}
else
{
this.MyParsingState = ParsingState.InvalidStructure;
return error;
}
this.MyParsingState = ParsingState.Parsed;
return MP4ParsingError.Success;
}
}
BoxBase:
public abstract class BoxBase : IBox
{
public ulong PositionInFile { get; set; }
private ulong MyDataSize;
public virtual ulong BoxContentEndOffset
{
get { return 0 + this.MyDataSize; }
}
public IParent Parent { get; private set; }
public virtual ICollection<IElementType> PossibleParents
{
get { return null; }
}
protected ParsingState MyParsingState { get; private set; }
public virtual bool IsParsingDone
{
get { return this.MyParsingState.IsParsingDone(); }
}
public virtual bool CanContinueParsing
{
get { return this.MyParsingState.CanContinueParsing(); }
}
public ulong Size { get; set; }
public IElementType<IBox> BoxType { get; set; }
public virtual MP4ParsingError TryParse(Stream stream, IParent parent, ulong boxPosition, ulong boxLength)
{
/* (MPEG-4 'Box' parsing; size and FourCC) */
}
}
IParent:
public interface IParent
{
IElementType ParentType { get; }
IDictionary<ICollection<IElementType>, Tuple<int, int>> RequiredElements { get; }
bool CheckChildAdditionalRequirements();
}
public interface IParent<TElement> : IParent
where TElement : IBox
{
ICollection<TElement> ChildElements { get; }
}
IBox:
public interface IBox
{
ulong PositionInFile { get; set; }
ulong BoxContentEndOffset { get; }
IParent Parent { get; }
ICollection<IElementType> PossibleParents { get; }
bool IsParsingDone { get; }
bool CanContinueParsing { get; }
ulong Size { get; set; }
IElementType<IBox> BoxType { get; set; }
MP4ParsingError TryParse(Stream stream, IParent parent, ulong boxPosition, ulong boxLength);
}