// Token: 0x06002BD0 RID: 11216 RVA: 0x000C779C File Offset: 0x000C599C private void _Initialize() { this._doclistHead = new ChildDocumentBlock(this, new NullTextContainer()); this._doclistTail = new ChildDocumentBlock(this, new NullTextContainer()); this._doclistHead.InsertNextBlock(this._doclistTail); ChildDocumentBlock childDocumentBlock = this._doclistHead; foreach (DocumentReference docRef in this._parent.References) { childDocumentBlock.InsertNextBlock(new ChildDocumentBlock(this, docRef)); childDocumentBlock = childDocumentBlock.NextBlock; } if (this._parent.References.Count != 0) { this._start = new DocumentSequenceTextPointer(this._doclistHead.NextBlock, this._doclistHead.NextBlock.ChildContainer.Start); this._end = new DocumentSequenceTextPointer(this._doclistTail.PreviousBlock, this._doclistTail.PreviousBlock.ChildContainer.End); } else { this._start = new DocumentSequenceTextPointer(this._doclistHead, this._doclistHead.ChildContainer.Start); this._end = new DocumentSequenceTextPointer(this._doclistTail, this._doclistTail.ChildContainer.End); } this._parent.References.CollectionChanged += this._OnContentChanged; this.Highlights.Changed += this._OnHighlightChanged; }
// Token: 0x06002BCD RID: 11213 RVA: 0x000C7730 File Offset: 0x000C5930 internal int GetChildBlockDistance(ChildDocumentBlock block1, ChildDocumentBlock block2) { if (block1 == block2) { return(0); } int num = 0; for (ChildDocumentBlock childDocumentBlock = block1; childDocumentBlock != null; childDocumentBlock = childDocumentBlock.NextBlock) { if (childDocumentBlock == block2) { return(num); } num++; } num = 0; for (ChildDocumentBlock childDocumentBlock = block1; childDocumentBlock != null; childDocumentBlock = childDocumentBlock.PreviousBlock) { if (childDocumentBlock == block2) { return(num); } num--; } return(0); }
//------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructors // Ctor always set mutable flag to false internal DocumentSequenceTextPointer(ChildDocumentBlock childBlock, ITextPointer childPosition) { Debug.Assert(childBlock != null); Debug.Assert(childPosition != null); _childBlock = childBlock; _childTp = childPosition; }
// Intelligent compare routine that understands block gap // Since there it is assumed that there is an invisible Gap // object between adjancent two blocks, there is no position // overlap. private static int xGapAwareCompareTo(DocumentSequenceTextPointer thisTp, DocumentSequenceTextPointer tp) { Debug.Assert(tp != null); if ((object)thisTp == (object)tp) { return(0); } ChildDocumentBlock thisBlock = thisTp.ChildBlock; ChildDocumentBlock tpBlock = tp.ChildBlock; int comp = thisTp.AggregatedContainer.GetChildBlockDistance(thisBlock, tpBlock); if (comp == 0) { Debug.Assert(thisTp.ChildBlock.ChildContainer == tp.ChildBlock.ChildContainer); return(thisTp.ChildPointer.CompareTo(tp.ChildPointer)); } else if (comp < 0) { // thisBlock is after tpBlock return(xUnseparated(tp, thisTp) ? 0 : 1); } else { // thisBlock is before tpBlock return(xUnseparated(thisTp, tp) ? 0 : -1); } }
// Token: 0x06002C1C RID: 11292 RVA: 0x000C8430 File Offset: 0x000C6630 private static int xGapAwareCompareTo(DocumentSequenceTextPointer thisTp, DocumentSequenceTextPointer tp) { if (thisTp == tp) { return(0); } ChildDocumentBlock childBlock = thisTp.ChildBlock; ChildDocumentBlock childBlock2 = tp.ChildBlock; int childBlockDistance = thisTp.AggregatedContainer.GetChildBlockDistance(childBlock, childBlock2); if (childBlockDistance == 0) { return(thisTp.ChildPointer.CompareTo(tp.ChildPointer)); } if (childBlockDistance < 0) { if (!DocumentSequenceTextPointer.xUnseparated(tp, thisTp)) { return(1); } return(0); } else { if (!DocumentSequenceTextPointer.xUnseparated(thisTp, tp)) { return(-1); } return(0); } }
// Token: 0x06002C19 RID: 11289 RVA: 0x000C8360 File Offset: 0x000C6560 private static DocumentSequenceTextPointer xGetClingDSTP(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { TextPointerContext pointerContext = thisTp.ChildPointer.GetPointerContext(direction); if (pointerContext != TextPointerContext.None) { return(thisTp); } ChildDocumentBlock childDocumentBlock = thisTp.ChildBlock; ITextPointer textPointer = thisTp.ChildPointer; if (direction == LogicalDirection.Forward) { while (pointerContext == TextPointerContext.None) { if (childDocumentBlock.IsTail) { break; } childDocumentBlock = childDocumentBlock.NextBlock; textPointer = childDocumentBlock.ChildContainer.Start; pointerContext = textPointer.GetPointerContext(direction); } } else { while (pointerContext == TextPointerContext.None && !childDocumentBlock.IsHead) { childDocumentBlock = childDocumentBlock.PreviousBlock; textPointer = childDocumentBlock.ChildContainer.End; pointerContext = textPointer.GetPointerContext(direction); } } return(new DocumentSequenceTextPointer(childDocumentBlock, textPointer)); }
// Token: 0x06002B3A RID: 11066 RVA: 0x000C5646 File Offset: 0x000C3846 internal ChildDocumentBlock InsertNextBlock(ChildDocumentBlock newBlock) { newBlock._nextBlock = this._nextBlock; newBlock._previousBlock = this; if (this._nextBlock != null) { this._nextBlock._previousBlock = newBlock; } this._nextBlock = newBlock; return(newBlock); }
// Token: 0x06002BD3 RID: 11219 RVA: 0x000C7B28 File Offset: 0x000C5D28 private void _OnHighlightChanged(object sender, HighlightChangedEventArgs args) { int i = 0; DocumentSequenceTextPointer documentSequenceTextPointer = null; ChildDocumentBlock childDocumentBlock = null; List <TextSegment> list = new List <TextSegment>(4); while (i < args.Ranges.Count) { TextSegment textSegment = (TextSegment)args.Ranges[i]; DocumentSequenceTextPointer documentSequenceTextPointer2 = (DocumentSequenceTextPointer)textSegment.End; if (documentSequenceTextPointer == null) { documentSequenceTextPointer = (DocumentSequenceTextPointer)textSegment.Start; } ChildDocumentBlock childDocumentBlock2 = childDocumentBlock; childDocumentBlock = documentSequenceTextPointer.ChildBlock; if (childDocumentBlock2 != null && childDocumentBlock != childDocumentBlock2 && !(childDocumentBlock2.ChildContainer is NullTextContainer) && list.Count != 0) { childDocumentBlock2.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(list)); list.Clear(); } ITextPointer childPointer = documentSequenceTextPointer.ChildPointer; if (documentSequenceTextPointer2.ChildBlock != childDocumentBlock) { ITextPointer textPointer = documentSequenceTextPointer.ChildPointer.TextContainer.End; if (childPointer.CompareTo(textPointer) != 0) { list.Add(new TextSegment(childPointer, textPointer)); } if (!(childDocumentBlock.ChildContainer is NullTextContainer) && list.Count != 0) { childDocumentBlock.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(list)); } childDocumentBlock = childDocumentBlock.NextBlock; documentSequenceTextPointer = new DocumentSequenceTextPointer(childDocumentBlock, childDocumentBlock.ChildContainer.Start); list.Clear(); } else { ITextPointer textPointer = documentSequenceTextPointer2.ChildPointer; if (childPointer.CompareTo(textPointer) != 0) { list.Add(new TextSegment(childPointer, textPointer)); } i++; documentSequenceTextPointer = null; } } if (list.Count > 0 && childDocumentBlock != null && !(childDocumentBlock.ChildContainer is NullTextContainer)) { childDocumentBlock.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(list)); } }
// Token: 0x06002BCC RID: 11212 RVA: 0x000C76FC File Offset: 0x000C58FC internal ChildDocumentBlock FindChildBlock(DocumentReference docRef) { for (ChildDocumentBlock nextBlock = this._doclistHead.NextBlock; nextBlock != null; nextBlock = nextBlock.NextBlock) { if (nextBlock.DocRef == docRef) { return(nextBlock); } } return(null); }
// Token: 0x06002BCB RID: 11211 RVA: 0x000C76C4 File Offset: 0x000C58C4 internal DocumentSequenceTextPointer MapChildPositionToParent(ITextPointer tp) { for (ChildDocumentBlock childDocumentBlock = this._doclistHead; childDocumentBlock != null; childDocumentBlock = childDocumentBlock.NextBlock) { if (childDocumentBlock.ChildContainer == tp.TextContainer) { return(new DocumentSequenceTextPointer(childDocumentBlock, tp)); } } return(null); }
/// <summary> /// Debug only ToString override. /// </summary> public override string ToString() { int blocks = 0; ChildDocumentBlock cdb = this._doclistHead; while (cdb != null) { blocks++; cdb = cdb.NextBlock; } return("DSTC Id=" + DebugId + " Blocks= " + blocks); }
//-------------------------------------------------------------------- // ContentChange //--------------------------------------------------------------------- private void _OnContentChanged(object sender, NotifyCollectionChangedEventArgs args) { #if DEBUG this._generation++; #endif if (args.Action == NotifyCollectionChangedAction.Add) { if (args.NewItems.Count != 1) { throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); } else { object item = args.NewItems[0]; int startingIndex = args.NewStartingIndex; if (startingIndex != _parent.References.Count - 1) { throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); } ChildDocumentBlock newBlock = new ChildDocumentBlock(this, (DocumentReference)item); ChildDocumentBlock insertAfter = _doclistTail.PreviousBlock; insertAfter.InsertNextBlock(newBlock); DocumentSequenceTextPointer changeStart = new DocumentSequenceTextPointer(insertAfter, insertAfter.End); //Update end pointer _end = new DocumentSequenceTextPointer(newBlock, newBlock.ChildContainer.End); if (newBlock.NextBlock == _doclistTail && newBlock.PreviousBlock == _doclistHead) { //Update start pointer for the first block _start = new DocumentSequenceTextPointer(newBlock, newBlock.ChildContainer.Start); } // Record Change Notifications ITextContainer container = newBlock.ChildContainer; int symbolCount = 1; // takes too long to calculate for large documents, and no one will use this info // this does not affect state, only fires event handlers AddChange(changeStart, symbolCount, PrecursorTextChangeType.ContentAdded); } } else { throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); } }
// Given an ITextPointer in a child TextContainer, create a position in parent's // address space to represent it. internal DocumentSequenceTextPointer MapChildPositionToParent(ITextPointer tp) { ChildDocumentBlock cdb = this._doclistHead; while (cdb != null) { if (cdb.ChildContainer == tp.TextContainer) { return(new DocumentSequenceTextPointer(cdb, tp)); } cdb = cdb.NextBlock; } return(null); }
// Token: 0x06002C1D RID: 11293 RVA: 0x000C8494 File Offset: 0x000C6694 private static bool xUnseparated(DocumentSequenceTextPointer tp1, DocumentSequenceTextPointer tp2) { if (tp1.ChildPointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None || tp2.ChildPointer.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.None) { return(false); } for (ChildDocumentBlock nextBlock = tp1.ChildBlock.NextBlock; nextBlock != tp2.ChildBlock; nextBlock = nextBlock.NextBlock) { if (nextBlock.ChildContainer.Start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) { return(false); } } return(true); }
// Find a ChildBlock in the list that corresponds to the given DocumentReference // Return null if cannot find internal ChildDocumentBlock FindChildBlock(DocumentReference docRef) { Debug.Assert(docRef != null); ChildDocumentBlock cdb = _doclistHead.NextBlock; while (cdb != null) { if (cdb.DocRef == docRef) { return(cdb); } cdb = cdb.NextBlock; } return(null); }
// Token: 0x06002C1E RID: 11294 RVA: 0x000C84F4 File Offset: 0x000C66F4 private static int xGapAwareGetDistance(DocumentSequenceTextPointer tp1, DocumentSequenceTextPointer tp2) { if (tp1 == tp2) { return(0); } int num = 0; DocumentSequenceTextPointer documentSequenceTextPointer = new DocumentSequenceTextPointer(tp1.ChildBlock, tp1.ChildPointer); while (documentSequenceTextPointer.ChildBlock != tp2.ChildBlock) { num += documentSequenceTextPointer.ChildPointer.GetOffsetToPosition(documentSequenceTextPointer.ChildPointer.TextContainer.End); ChildDocumentBlock nextBlock = documentSequenceTextPointer.ChildBlock.NextBlock; documentSequenceTextPointer.ChildBlock = nextBlock; documentSequenceTextPointer.ChildPointer = nextBlock.ChildContainer.Start; } return(num + documentSequenceTextPointer.ChildPointer.GetOffsetToPosition(tp2.ChildPointer)); }
//-------------------------------------------------------------------- // // Internal Properties // //--------------------------------------------------------------------- #region Internal Methods // Insert a new node into this list. internal ChildDocumentBlock InsertNextBlock(ChildDocumentBlock newBlock) { Debug.Assert(newBlock != null); // Setup the new block correctly newBlock._nextBlock = this._nextBlock; newBlock._previousBlock = this; // Link old next block to the new block if (this._nextBlock != null) { this._nextBlock._previousBlock = newBlock; } // Link this block to new block this._nextBlock = newBlock; return newBlock; }
//-------------------------------------------------------------------- // // Internal Properties // //--------------------------------------------------------------------- #region Internal Methods // Insert a new node into this list. internal ChildDocumentBlock InsertNextBlock(ChildDocumentBlock newBlock) { Debug.Assert(newBlock != null); // Setup the new block correctly newBlock._nextBlock = this._nextBlock; newBlock._previousBlock = this; // Link old next block to the new block if (this._nextBlock != null) { this._nextBlock._previousBlock = newBlock; } // Link this block to new block this._nextBlock = newBlock; return(newBlock); }
// Token: 0x06002B7B RID: 11131 RVA: 0x000C67A8 File Offset: 0x000C49A8 internal ContentPosition GetObjectPosition(object o) { if (o == null) { throw new ArgumentNullException("o"); } foreach (DocumentReference docRef in this.References) { DynamicDocumentPaginator paginator = this.GetPaginator(docRef); if (paginator != null) { ContentPosition objectPosition = paginator.GetObjectPosition(o); if (objectPosition != ContentPosition.Missing && objectPosition is ITextPointer) { ChildDocumentBlock childBlock = new ChildDocumentBlock(this.TextContainer, docRef); return(new DocumentSequenceTextPointer(childBlock, (ITextPointer)objectPosition)); } } } return(ContentPosition.Missing); }
//-------------------------------------------------------------------- // // Private Methods // //--------------------------------------------------------------------- #region Private Methods //-------------------------------------------------------------------- // Initilization //--------------------------------------------------------------------- private void _Initialize() { Debug.Assert(_parent != null); // Create Start Block/Container/Position _doclistHead = new ChildDocumentBlock(this, new NullTextContainer()); // Create End Block/Container/Position _doclistTail = new ChildDocumentBlock(this, new NullTextContainer()); // Link Start and End container together _doclistHead.InsertNextBlock(_doclistTail); // Now initialize the child doc block list ChildDocumentBlock currentBlock = _doclistHead; foreach (DocumentReference docRef in _parent.References) { currentBlock.InsertNextBlock(new ChildDocumentBlock(this, docRef)); currentBlock = currentBlock.NextBlock; } //if we have at least one document, start and end pointers should be set to valid child blocks not to the placeholders if (_parent.References.Count != 0) { _start = new DocumentSequenceTextPointer(_doclistHead.NextBlock, _doclistHead.NextBlock.ChildContainer.Start); _end = new DocumentSequenceTextPointer(_doclistTail.PreviousBlock, _doclistTail.PreviousBlock.ChildContainer.End); } else { _start = new DocumentSequenceTextPointer(_doclistHead, _doclistHead.ChildContainer.Start); _end = new DocumentSequenceTextPointer(_doclistTail, _doclistTail.ChildContainer.End); } // Listen to collection changes _parent.References.CollectionChanged += new NotifyCollectionChangedEventHandler(_OnContentChanged); // Listen to Highlights changes so that it can notify sub-TextContainer this.Highlights.Changed += new HighlightChangedEventHandler(_OnHighlightChanged); }
// Return distance between two child TextContainer // 0 means the same container // n means block1 is n steps before block2 in the link list // -n means block1 is n steps after block2 in the link list internal int GetChildBlockDistance(ChildDocumentBlock block1, ChildDocumentBlock block2) { // Note: we can improve perf of this function by using generation // mark + caching index, if this function turns out to be costly. // However, I would expect it to not happen very often. if ((object)block1 == (object)block2) { return(0); } // Assuming block1 is before block2 int count = 0; ChildDocumentBlock cdb = block1; while (cdb != null) { if (cdb == block2) { return(count); } count++; cdb = cdb.NextBlock; } // Now block2 has to be before block1 count = 0; cdb = block1; while (cdb != null) { if (cdb == block2) { return(count); } count--; cdb = cdb.PreviousBlock; } Debug.Assert(false, "should never be here"); return(0); }
// Token: 0x06002BD2 RID: 11218 RVA: 0x000C79F0 File Offset: 0x000C5BF0 private void _OnContentChanged(object sender, NotifyCollectionChangedEventArgs args) { if (args.Action != NotifyCollectionChangedAction.Add) { throw new NotSupportedException(SR.Get("UnexpectedCollectionChangeAction", new object[] { args.Action })); } if (args.NewItems.Count != 1) { throw new NotSupportedException(SR.Get("RangeActionsNotSupported")); } object obj = args.NewItems[0]; int newStartingIndex = args.NewStartingIndex; if (newStartingIndex != this._parent.References.Count - 1) { throw new NotSupportedException(SR.Get("UnexpectedCollectionChangeAction", new object[] { args.Action })); } ChildDocumentBlock childDocumentBlock = new ChildDocumentBlock(this, (DocumentReference)obj); ChildDocumentBlock previousBlock = this._doclistTail.PreviousBlock; previousBlock.InsertNextBlock(childDocumentBlock); DocumentSequenceTextPointer startPosition = new DocumentSequenceTextPointer(previousBlock, previousBlock.End); this._end = new DocumentSequenceTextPointer(childDocumentBlock, childDocumentBlock.ChildContainer.End); if (childDocumentBlock.NextBlock == this._doclistTail && childDocumentBlock.PreviousBlock == this._doclistHead) { this._start = new DocumentSequenceTextPointer(childDocumentBlock, childDocumentBlock.ChildContainer.Start); } ITextContainer childContainer = childDocumentBlock.ChildContainer; int symbolCount = 1; this.AddChange(startPosition, symbolCount, PrecursorTextChangeType.ContentAdded); }
//------------------------------------------------------ // // Internal Property // //------------------------------------------------------ //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods private static DocumentSequenceTextPointer xGetClingDSTP(DocumentSequenceTextPointer thisTp, LogicalDirection direction) { TextPointerContext context = thisTp.ChildPointer.GetPointerContext(direction); if (context != TextPointerContext.None) { return(thisTp); } ChildDocumentBlock block = thisTp.ChildBlock; ITextPointer pointer = thisTp.ChildPointer; if (direction == LogicalDirection.Forward) { while (context == TextPointerContext.None && !block.IsTail) { // get next block block = block.NextBlock; // get start pointer = block.ChildContainer.Start; context = pointer.GetPointerContext(direction); } } else { Debug.Assert(direction == LogicalDirection.Backward); while (context == TextPointerContext.None && !block.IsHead) { // get next block block = block.PreviousBlock; // get start pointer = block.ChildContainer.End; context = pointer.GetPointerContext(direction); } } return(new DocumentSequenceTextPointer(block, pointer)); }
private static bool xUnseparated(DocumentSequenceTextPointer tp1, DocumentSequenceTextPointer tp2) { // tp1 is before tp2, check both are at edge of documents //check nothing of any length between them if (tp1.ChildPointer.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None || tp2.ChildPointer.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.None) { return(false); } ChildDocumentBlock block = tp1.ChildBlock.NextBlock; while (block != tp2.ChildBlock) { if (block.ChildContainer.Start.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.None) { return(false); } block = block.NextBlock; } return(true); }
// Get the count of symbols between two TP. // Gap aware // TP1 <= TP2 private static int xGapAwareGetDistance(DocumentSequenceTextPointer tp1, DocumentSequenceTextPointer tp2) { Debug.Assert(xGapAwareCompareTo(tp1, tp2) <= 0); if (tp1 == tp2) { return(0); } int count = 0; DocumentSequenceTextPointer tpScan = new DocumentSequenceTextPointer(tp1.ChildBlock, tp1.ChildPointer); while (tpScan.ChildBlock != tp2.ChildBlock) { // Skip the entire block to the end count += tpScan.ChildPointer.GetOffsetToPosition(tpScan.ChildPointer.TextContainer.End); // Move on to next block ChildDocumentBlock nextBlock = tpScan.ChildBlock.NextBlock; tpScan.ChildBlock = nextBlock; tpScan.ChildPointer = nextBlock.ChildContainer.Start; } count += tpScan.ChildPointer.GetOffsetToPosition(tp2.ChildPointer); return(count); }
//-------------------------------------------------------------------- // Highlight compositing //--------------------------------------------------------------------- private void _OnHighlightChanged(object sender, HighlightChangedEventArgs args) { Debug.Assert(sender != null); Debug.Assert(args != null); Debug.Assert(args.Ranges != null); #if DEBUG { Highlights highlights = this.Highlights; StaticTextPointer highlightTransitionPosition; StaticTextPointer highlightRangeStart; object selected; DocumentsTrace.FixedDocumentSequence.Highlights.Trace("===BeginNewHighlightRange==="); highlightTransitionPosition = ((ITextContainer)this).CreateStaticPointerAtOffset(0); while (true) { // Move to the next highlight start. if (!highlights.IsContentHighlighted(highlightTransitionPosition, LogicalDirection.Forward)) { highlightTransitionPosition = highlights.GetNextHighlightChangePosition(highlightTransitionPosition, LogicalDirection.Forward); // No more highlights? if (highlightTransitionPosition.IsNull) { break; } } // highlightTransitionPosition is at the start of a new highlight run. selected = highlights.GetHighlightValue(highlightTransitionPosition, LogicalDirection.Forward, typeof(TextSelection)); // Save the start position and find the end. highlightRangeStart = highlightTransitionPosition; highlightTransitionPosition = highlights.GetNextHighlightChangePosition(highlightTransitionPosition, LogicalDirection.Forward); Invariant.Assert(!highlightTransitionPosition.IsNull, "Highlight start not followed by highlight end!"); // Store the highlight. if (selected != DependencyProperty.UnsetValue) { DocumentsTrace.FixedDocumentSequence.Highlights.Trace(string.Format("HightlightRange {0}-{1}", highlightRangeStart.ToString(), highlightTransitionPosition.ToString())); if (highlightRangeStart.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text) { DocumentsTrace.FixedDocumentSequence.Highlights.Trace("<HighlightNotOnText>"); } else { char[] sb = new char[256]; TextPointerBase.GetTextWithLimit(highlightRangeStart.CreateDynamicTextPointer(LogicalDirection.Forward), LogicalDirection.Forward, sb, 0, 256, highlightTransitionPosition.CreateDynamicTextPointer(LogicalDirection.Forward)); DocumentsTrace.FixedDocumentSequence.TextOM.Trace(string.Format("HightlightContent [{0}]", new String(sb))); } } } DocumentsTrace.FixedDocumentSequence.TextOM.Trace("===EndNewHighlightRange==="); } #endif Debug.Assert(args.Ranges.Count > 0 && ((TextSegment)args.Ranges[0]).Start.CompareTo(((TextSegment)args.Ranges[0]).End) < 0); // For each change range we received, we need to figure out // affected child TextContainer, and notify it with appropriate // ranges that are in the child's address space. // // We only fire one highlight change notification for any child // TextContainer even if there is multiple change ranges fall // into the same child TextContainer. // // We scan the ranges and the child TextContainer in the same loop, // moving forward two scanning pointers and at boundary of each // TextContainer, we fire a change notification. // int idxScan = 0; DocumentSequenceTextPointer tsScan = null; ChildDocumentBlock cdbScan = null; List <TextSegment> rangeArray = new List <TextSegment>(4); while (idxScan < args.Ranges.Count) { TextSegment ts = (TextSegment)args.Ranges[idxScan]; DocumentSequenceTextPointer tsEnd = (DocumentSequenceTextPointer)ts.End; ITextPointer tpChildStart, tpChildEnd; ChildDocumentBlock lastBlock; // If tsScan == null, we were done with previous range, // so we are going to set tsScan to begining of this range. // Otherwise the previous range was split so we will simply // start from what was left over from previous loop. if (tsScan == null) { tsScan = (DocumentSequenceTextPointer)ts.Start; } lastBlock = cdbScan; cdbScan = tsScan.ChildBlock; if (lastBlock != null && cdbScan != lastBlock && !(lastBlock.ChildContainer is NullTextContainer) && rangeArray.Count != 0) { // This range is in a different block, so take care of old blocks first lastBlock.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(rangeArray)); rangeArray.Clear(); } tpChildStart = tsScan.ChildPointer; if (tsEnd.ChildBlock != cdbScan) { // If this range crosses blocks, we are done with current block tpChildEnd = tsScan.ChildPointer.TextContainer.End; if (tpChildStart.CompareTo(tpChildEnd) != 0) { rangeArray.Add(new TextSegment(tpChildStart, tpChildEnd)); } // Notify child container if (!(cdbScan.ChildContainer is NullTextContainer) && rangeArray.Count != 0) { cdbScan.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(rangeArray)); } // Move on to next block; cdbScan = cdbScan.NextBlock; tsScan = new DocumentSequenceTextPointer(cdbScan, cdbScan.ChildContainer.Start); rangeArray.Clear(); } else { // Otherwise we need to go on to see if there is more ranges // fall withing the same block. Simply add this change range tpChildEnd = tsEnd.ChildPointer; if (tpChildStart.CompareTo(tpChildEnd) != 0) { rangeArray.Add(new TextSegment(tpChildStart, tpChildEnd)); } // Move on to next range idxScan++; tsScan = null; } } // Fire change notification for the last child block. if (rangeArray.Count > 0 && (!(cdbScan == null || cdbScan.ChildContainer is NullTextContainer))) { cdbScan.ChildHighlightLayer.RaiseHighlightChangedEvent(new ReadOnlyCollection <TextSegment>(rangeArray)); } }
// Move this TP by distance, and respect virtualization of child TextContainer // Return true if distance is within boundary of the aggregated container, false otherwise private static bool xGapAwareScan(DocumentSequenceTextPointer thisTp, int distance) { // // Note: To calculate distance between thisTp.ChildPointer to // it container Start/End position would have been devastating // for those who implemented vitualization. // Ideally we would need a new API on ITextPointer // ITextPointer.IsDistanceOutOfRange ChildDocumentBlock cdb = thisTp.ChildBlock; bool isNavigator = true; ITextPointer childTn = thisTp.ChildPointer; if (childTn == null) { isNavigator = false; childTn = thisTp.ChildPointer.CreatePointer(); } LogicalDirection scanDir = (distance > 0 ? LogicalDirection.Forward : LogicalDirection.Backward); distance = Math.Abs(distance); while (distance > 0) { TextPointerContext tst = childTn.GetPointerContext(scanDir); switch (tst) { case TextPointerContext.ElementStart: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.ElementEnd: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.EmbeddedElement: childTn.MoveToNextContextPosition(scanDir); distance--; break; case TextPointerContext.Text: int runLength = childTn.GetTextRunLength(scanDir); int moveLength = runLength < distance ? runLength : distance; distance -= moveLength; //agurcan: Fix for 1098225 //We need to propagate direction info to MoveByOffset if (scanDir == LogicalDirection.Backward) { moveLength *= -1; } childTn.MoveByOffset(moveLength); break; case TextPointerContext.None: if (!((cdb.IsHead && scanDir == LogicalDirection.Backward) || (cdb.IsTail && scanDir == LogicalDirection.Forward) ) ) { cdb = (scanDir == LogicalDirection.Forward ? cdb.NextBlock : cdb.PreviousBlock); childTn = (scanDir == LogicalDirection.Forward ? cdb.ChildContainer.Start.CreatePointer(childTn.LogicalDirection) : cdb.ChildContainer.End.CreatePointer(childTn.LogicalDirection) ); } else { return(false); } break; default: Debug.Assert(false, "invalid TextPointerContext"); break; } } // Re-position thisTp to the new location. thisTp.ChildBlock = cdb; if (isNavigator) { thisTp.ChildPointer = childTn; } else { thisTp.ChildPointer = childTn.CreatePointer(); } return(true); }
//------------------------------------------------------------------- // // Private Methods // //---------------------------------------------------------------------- #region Private Methods //------------------------------------------------------------------- // Initilization //--------------------------------------------------------------------- private void _Initialize() { Debug.Assert(_parent != null); // Create Start Block/Container/Position _doclistHead = new ChildDocumentBlock(this, new NullTextContainer()); // Create End Block/Container/Position _doclistTail = new ChildDocumentBlock(this, new NullTextContainer()); // Link Start and End container together _doclistHead.InsertNextBlock(_doclistTail); // Now initialize the child doc block list ChildDocumentBlock currentBlock = _doclistHead; foreach (DocumentReference docRef in _parent.References) { currentBlock.InsertNextBlock(new ChildDocumentBlock(this, docRef)); currentBlock = currentBlock.NextBlock; } //if we have at least one document, start and end pointers should be set to valid child blocks not to the placeholders if (_parent.References.Count != 0) { _start = new DocumentSequenceTextPointer(_doclistHead.NextBlock, _doclistHead.NextBlock.ChildContainer.Start); _end = new DocumentSequenceTextPointer(_doclistTail.PreviousBlock, _doclistTail.PreviousBlock.ChildContainer.End); } else { _start = new DocumentSequenceTextPointer(_doclistHead, _doclistHead.ChildContainer.Start); _end = new DocumentSequenceTextPointer(_doclistTail, _doclistTail.ChildContainer.End); } // Listen to collection changes _parent.References.CollectionChanged += new NotifyCollectionChangedEventHandler(_OnContentChanged); // Listen to Highlights changes so that it can notify sub-TextContainer this.Highlights.Changed += new HighlightChangedEventHandler(_OnHighlightChanged); }
//Searches for the specified pattern and updates start *or* end pointers depending on search direction //At the end of the operation, start or end should be pointing to the beginning/end of the page //of occurance of pattern respectively internal static TextRange Find(ITextPointer start, ITextPointer end, string findPattern, CultureInfo cultureInfo, bool matchCase, bool matchWholeWord, bool matchLast, bool matchDiacritics, bool matchKashida, bool matchAlefHamza) { Debug.Assert(start != null); Debug.Assert(end != null); Debug.Assert(((start is DocumentSequenceTextPointer) && (end is DocumentSequenceTextPointer)) || ((start is FixedTextPointer) && (end is FixedTextPointer))); Debug.Assert(findPattern != null); if (findPattern.Length == 0) { return(null); } IDocumentPaginatorSource paginatorSource = start.TextContainer.Parent as IDocumentPaginatorSource; DynamicDocumentPaginator paginator = paginatorSource.DocumentPaginator as DynamicDocumentPaginator; Debug.Assert(paginator != null); int pageNumber = -1; int endPageNumber = -1; if (matchLast) { endPageNumber = paginator.GetPageNumber((ContentPosition)start); pageNumber = paginator.GetPageNumber((ContentPosition)end); } else { endPageNumber = paginator.GetPageNumber((ContentPosition)end); pageNumber = paginator.GetPageNumber((ContentPosition)start); } TextRange result = null; CompareInfo compareInfo = cultureInfo.CompareInfo; bool replaceAlefWithAlefHamza = false; CompareOptions compareOptions = _InitializeSearch(cultureInfo, matchCase, matchAlefHamza, matchDiacritics, ref findPattern, out replaceAlefWithAlefHamza); //Translate the page number int translatedPageNumber = pageNumber; //If this is a DocumentSequence, we need to pass translated page number to the below call FixedDocumentSequence documentSequence = paginatorSource as FixedDocumentSequence; DynamicDocumentPaginator childPaginator = null; if (documentSequence != null) { documentSequence.TranslatePageNumber(pageNumber, out childPaginator, out translatedPageNumber); } if (pageNumber - endPageNumber != 0) { ITextPointer firstSearchPageStart = null; ITextPointer firstSearchPageEnd = null; _GetFirstPageSearchPointers(start, end, translatedPageNumber, matchLast, out firstSearchPageStart, out firstSearchPageEnd); Debug.Assert(firstSearchPageStart != null); Debug.Assert(firstSearchPageEnd != null); //Need to search the first page using TextFindEngine to start exactly from the requested search location to avoid false positives result = TextFindEngine.InternalFind(firstSearchPageStart, firstSearchPageEnd, findPattern, cultureInfo, matchCase, matchWholeWord, matchLast, matchDiacritics, matchKashida, matchAlefHamza); if (result == null) { //Start from the next page and check all pages until the end pageNumber = matchLast ? pageNumber - 1 : pageNumber + 1; int increment = matchLast ? -1 : 1; for (; matchLast?pageNumber >= endPageNumber : pageNumber <= endPageNumber; pageNumber += increment) { FixedDocument fixedDoc = null; translatedPageNumber = pageNumber; childPaginator = null; if (documentSequence != null) { documentSequence.TranslatePageNumber(pageNumber, out childPaginator, out translatedPageNumber); fixedDoc = (FixedDocument)childPaginator.Source; } else { fixedDoc = paginatorSource as FixedDocument; } Debug.Assert(fixedDoc != null); String pageString = _GetPageString(fixedDoc, translatedPageNumber, replaceAlefWithAlefHamza); if (pageString == null) { //This is not a page-per-stream //Default back to slow search return(TextFindEngine.InternalFind(start, end, findPattern, cultureInfo, matchCase, matchWholeWord, matchLast, matchDiacritics, matchKashida, matchAlefHamza)); } if (_FoundOnPage(pageString, findPattern, cultureInfo, compareOptions)) { //Update end or start pointer depending on search direction if (documentSequence != null) { ChildDocumentBlock childBlock = documentSequence.TextContainer.FindChildBlock(fixedDoc.DocumentReference); if (matchLast) { end = new DocumentSequenceTextPointer(childBlock, new FixedTextPointer(false, LogicalDirection.Backward, fixedDoc.FixedContainer.FixedTextBuilder.GetPageEndFlowPosition(translatedPageNumber))); start = new DocumentSequenceTextPointer(childBlock, new FixedTextPointer(false, LogicalDirection.Forward, fixedDoc.FixedContainer.FixedTextBuilder.GetPageStartFlowPosition(translatedPageNumber))); } else { start = new DocumentSequenceTextPointer(childBlock, new FixedTextPointer(false, LogicalDirection.Forward, fixedDoc.FixedContainer.FixedTextBuilder.GetPageStartFlowPosition(translatedPageNumber))); end = new DocumentSequenceTextPointer(childBlock, new FixedTextPointer(false, LogicalDirection.Backward, fixedDoc.FixedContainer.FixedTextBuilder.GetPageEndFlowPosition(translatedPageNumber))); } } else { //We are working on a FixedDocument FixedTextBuilder textBuilder = ((FixedDocument)(paginatorSource)).FixedContainer.FixedTextBuilder; if (matchLast) { end = new FixedTextPointer(false, LogicalDirection.Backward, textBuilder.GetPageEndFlowPosition(pageNumber)); start = new FixedTextPointer(false, LogicalDirection.Forward, textBuilder.GetPageStartFlowPosition(pageNumber)); } else { start = new FixedTextPointer(false, LogicalDirection.Forward, textBuilder.GetPageStartFlowPosition(pageNumber)); end = new FixedTextPointer(false, LogicalDirection.Backward, textBuilder.GetPageEndFlowPosition(pageNumber)); } } result = TextFindEngine.InternalFind(start, end, findPattern, cultureInfo, matchCase, matchWholeWord, matchLast, matchDiacritics, matchKashida, matchAlefHamza); //If the result is null, this means we had a false positive if (result != null) { return(result); } } } } } else { //Make sure fast search result and slow search result are consistent FixedDocument fixedDoc = childPaginator != null ? childPaginator.Source as FixedDocument : paginatorSource as FixedDocument; String pageString = _GetPageString(fixedDoc, translatedPageNumber, replaceAlefWithAlefHamza); if (pageString == null || _FoundOnPage(pageString, findPattern, cultureInfo, compareOptions)) { //The search is only limited to the current page result = TextFindEngine.InternalFind(start, end, findPattern, cultureInfo, matchCase, matchWholeWord, matchLast, matchDiacritics, matchKashida, matchAlefHamza); } } return(result); }
// Token: 0x06002E71 RID: 11889 RVA: 0x000D23C8 File Offset: 0x000D05C8 internal static TextRange Find(ITextPointer start, ITextPointer end, string findPattern, CultureInfo cultureInfo, bool matchCase, bool matchWholeWord, bool matchLast, bool matchDiacritics, bool matchKashida, bool matchAlefHamza) { if (findPattern.Length == 0) { return(null); } IDocumentPaginatorSource documentPaginatorSource = start.TextContainer.Parent as IDocumentPaginatorSource; DynamicDocumentPaginator dynamicDocumentPaginator = documentPaginatorSource.DocumentPaginator as DynamicDocumentPaginator; int pageNumber; int num; if (matchLast) { pageNumber = dynamicDocumentPaginator.GetPageNumber((ContentPosition)start); num = dynamicDocumentPaginator.GetPageNumber((ContentPosition)end); } else { pageNumber = dynamicDocumentPaginator.GetPageNumber((ContentPosition)end); num = dynamicDocumentPaginator.GetPageNumber((ContentPosition)start); } TextRange textRange = null; CompareInfo compareInfo = cultureInfo.CompareInfo; bool replaceAlefWithAlefHamza = false; CompareOptions compareOptions = FixedFindEngine._InitializeSearch(cultureInfo, matchCase, matchAlefHamza, matchDiacritics, ref findPattern, out replaceAlefWithAlefHamza); int num2 = num; FixedDocumentSequence fixedDocumentSequence = documentPaginatorSource as FixedDocumentSequence; DynamicDocumentPaginator dynamicDocumentPaginator2 = null; if (fixedDocumentSequence != null) { fixedDocumentSequence.TranslatePageNumber(num, out dynamicDocumentPaginator2, out num2); } if (num - pageNumber != 0) { ITextPointer startPosition = null; ITextPointer endPosition = null; FixedFindEngine._GetFirstPageSearchPointers(start, end, num2, matchLast, out startPosition, out endPosition); textRange = TextFindEngine.InternalFind(startPosition, endPosition, findPattern, cultureInfo, matchCase, matchWholeWord, matchLast, matchDiacritics, matchKashida, matchAlefHamza); if (textRange == null) { num = (matchLast ? (num - 1) : (num + 1)); int num3 = matchLast ? -1 : 1; while (matchLast ? (num >= pageNumber) : (num <= pageNumber)) { num2 = num; dynamicDocumentPaginator2 = null; FixedDocument fixedDocument; if (fixedDocumentSequence != null) { fixedDocumentSequence.TranslatePageNumber(num, out dynamicDocumentPaginator2, out num2); fixedDocument = (FixedDocument)dynamicDocumentPaginator2.Source; } else { fixedDocument = (documentPaginatorSource as FixedDocument); } string text = FixedFindEngine._GetPageString(fixedDocument, num2, replaceAlefWithAlefHamza); if (text == null) { return(TextFindEngine.InternalFind(start, end, findPattern, cultureInfo, matchCase, matchWholeWord, matchLast, matchDiacritics, matchKashida, matchAlefHamza)); } if (FixedFindEngine._FoundOnPage(text, findPattern, cultureInfo, compareOptions)) { if (fixedDocumentSequence != null) { ChildDocumentBlock childBlock = fixedDocumentSequence.TextContainer.FindChildBlock(fixedDocument.DocumentReference); if (matchLast) { end = new DocumentSequenceTextPointer(childBlock, new FixedTextPointer(false, LogicalDirection.Backward, fixedDocument.FixedContainer.FixedTextBuilder.GetPageEndFlowPosition(num2))); start = new DocumentSequenceTextPointer(childBlock, new FixedTextPointer(false, LogicalDirection.Forward, fixedDocument.FixedContainer.FixedTextBuilder.GetPageStartFlowPosition(num2))); } else { start = new DocumentSequenceTextPointer(childBlock, new FixedTextPointer(false, LogicalDirection.Forward, fixedDocument.FixedContainer.FixedTextBuilder.GetPageStartFlowPosition(num2))); end = new DocumentSequenceTextPointer(childBlock, new FixedTextPointer(false, LogicalDirection.Backward, fixedDocument.FixedContainer.FixedTextBuilder.GetPageEndFlowPosition(num2))); } } else { FixedTextBuilder fixedTextBuilder = ((FixedDocument)documentPaginatorSource).FixedContainer.FixedTextBuilder; if (matchLast) { end = new FixedTextPointer(false, LogicalDirection.Backward, fixedTextBuilder.GetPageEndFlowPosition(num)); start = new FixedTextPointer(false, LogicalDirection.Forward, fixedTextBuilder.GetPageStartFlowPosition(num)); } else { start = new FixedTextPointer(false, LogicalDirection.Forward, fixedTextBuilder.GetPageStartFlowPosition(num)); end = new FixedTextPointer(false, LogicalDirection.Backward, fixedTextBuilder.GetPageEndFlowPosition(num)); } } textRange = TextFindEngine.InternalFind(start, end, findPattern, cultureInfo, matchCase, matchWholeWord, matchLast, matchDiacritics, matchKashida, matchAlefHamza); if (textRange != null) { return(textRange); } } num += num3; } } } else { FixedDocument doc = (dynamicDocumentPaginator2 != null) ? (dynamicDocumentPaginator2.Source as FixedDocument) : (documentPaginatorSource as FixedDocument); string text2 = FixedFindEngine._GetPageString(doc, num2, replaceAlefWithAlefHamza); if (text2 == null || FixedFindEngine._FoundOnPage(text2, findPattern, cultureInfo, compareOptions)) { textRange = TextFindEngine.InternalFind(start, end, findPattern, cultureInfo, matchCase, matchWholeWord, matchLast, matchDiacritics, matchKashida, matchAlefHamza); } } return(textRange); }
// Return distance between two child TextContainer // 0 means the same container // n means block1 is n steps before block2 in the link list // -n means block1 is n steps after block2 in the link list internal int GetChildBlockDistance(ChildDocumentBlock block1, ChildDocumentBlock block2) { // Note: we can improve perf of this function by using generation // mark + caching index, if this function turns out to be costly. // However, I would expect it to not happen very often. if ((object)block1 == (object)block2) { return 0; } // Assuming block1 is before block2 int count = 0; ChildDocumentBlock cdb = block1; while (cdb != null) { if (cdb == block2) { return count; } count++; cdb = cdb.NextBlock; } // Now block2 has to be before block1 count = 0; cdb = block1; while (cdb != null) { if (cdb == block2) { return count; } count--; cdb = cdb.PreviousBlock; } Debug.Assert(false, "should never be here"); return 0; }
/// <summary> /// <see cref="DynamicDocumentPaginator.GetObjectPosition"/> /// </summary> internal ContentPosition GetObjectPosition(Object o) { if (o == null) { throw new ArgumentNullException("o"); } foreach (DocumentReference docRef in References) { DynamicDocumentPaginator childPaginator = GetPaginator(docRef); if (childPaginator != null) { ContentPosition cp = childPaginator.GetObjectPosition(o); if (cp != ContentPosition.Missing && (cp is ITextPointer)) { ChildDocumentBlock childBlock = new ChildDocumentBlock(this.TextContainer, docRef); return new DocumentSequenceTextPointer(childBlock, (ITextPointer)cp); } } } return ContentPosition.Missing; }
// Token: 0x06002BD4 RID: 11220 RVA: 0x000C7CA2 File Offset: 0x000C5EA2 internal DocumentSequenceTextPointer(ChildDocumentBlock childBlock, ITextPointer childPosition) { this._childBlock = childBlock; this._childTp = childPosition; }
// Token: 0x06002C1F RID: 11295 RVA: 0x000C8584 File Offset: 0x000C6784 private static bool xGapAwareScan(DocumentSequenceTextPointer thisTp, int distance) { ChildDocumentBlock childDocumentBlock = thisTp.ChildBlock; bool flag = true; ITextPointer textPointer = thisTp.ChildPointer; if (textPointer == null) { flag = false; textPointer = thisTp.ChildPointer.CreatePointer(); } LogicalDirection logicalDirection = (distance > 0) ? LogicalDirection.Forward : LogicalDirection.Backward; distance = Math.Abs(distance); while (distance > 0) { switch (textPointer.GetPointerContext(logicalDirection)) { case TextPointerContext.None: if ((childDocumentBlock.IsHead && logicalDirection == LogicalDirection.Backward) || (childDocumentBlock.IsTail && logicalDirection == LogicalDirection.Forward)) { return(false); } childDocumentBlock = ((logicalDirection == LogicalDirection.Forward) ? childDocumentBlock.NextBlock : childDocumentBlock.PreviousBlock); textPointer = ((logicalDirection == LogicalDirection.Forward) ? childDocumentBlock.ChildContainer.Start.CreatePointer(textPointer.LogicalDirection) : childDocumentBlock.ChildContainer.End.CreatePointer(textPointer.LogicalDirection)); break; case TextPointerContext.Text: { int textRunLength = textPointer.GetTextRunLength(logicalDirection); int num = (textRunLength < distance) ? textRunLength : distance; distance -= num; if (logicalDirection == LogicalDirection.Backward) { num *= -1; } textPointer.MoveByOffset(num); break; } case TextPointerContext.EmbeddedElement: textPointer.MoveToNextContextPosition(logicalDirection); distance--; break; case TextPointerContext.ElementStart: textPointer.MoveToNextContextPosition(logicalDirection); distance--; break; case TextPointerContext.ElementEnd: textPointer.MoveToNextContextPosition(logicalDirection); distance--; break; } } thisTp.ChildBlock = childDocumentBlock; if (flag) { thisTp.ChildPointer = textPointer; } else { thisTp.ChildPointer = textPointer.CreatePointer(); } return(true); }
//------------------------------------------------------------------- // ContentChange //---------------------------------------------------------------------- private void _OnContentChanged(object sender, NotifyCollectionChangedEventArgs args) { #if DEBUG this._generation++; #endif if (args.Action == NotifyCollectionChangedAction.Add) { if (args.NewItems.Count != 1) { throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); } else { object item = args.NewItems[0]; int startingIndex = args.NewStartingIndex; if (startingIndex != _parent.References.Count - 1) { throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); } ChildDocumentBlock newBlock = new ChildDocumentBlock(this, (DocumentReference)item); ChildDocumentBlock insertAfter = _doclistTail.PreviousBlock; insertAfter.InsertNextBlock(newBlock); DocumentSequenceTextPointer changeStart = new DocumentSequenceTextPointer(insertAfter, insertAfter.End); //Update end pointer _end = new DocumentSequenceTextPointer(newBlock, newBlock.ChildContainer.End); if (newBlock.NextBlock == _doclistTail && newBlock.PreviousBlock == _doclistHead) { //Update start pointer for the first block _start = new DocumentSequenceTextPointer(newBlock, newBlock.ChildContainer.Start); } // Record Change Notifications ITextContainer container = newBlock.ChildContainer; int symbolCount = 1; // takes too long to calculate for large documents, and no one will use this info // this does not affect state, only fires event handlers AddChange(changeStart, symbolCount, PrecursorTextChangeType.ContentAdded); } } else { throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); } }