diff options
Diffstat (limited to 'bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentReParser.java')
-rw-r--r-- | bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentReParser.java | 1658 |
1 files changed, 0 insertions, 1658 deletions
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentReParser.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentReParser.java deleted file mode 100644 index f846682f8a..0000000000 --- a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/text/StructuredDocumentReParser.java +++ /dev/null @@ -1,1658 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2001, 2010 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - * Jens Lukowski/Innoopract - initial renaming/restructuring - * David Carver (Intalio) - bug 300427 - Comparison of String Objects using == or != - *******************************************************************************/ -package org.eclipse.wst.sse.core.internal.text; - -import java.util.Iterator; -import java.util.List; - -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.FindReplaceDocumentAdapter; -import org.eclipse.jface.text.IRegion; -import org.eclipse.wst.sse.core.internal.Logger; -import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTagParser; -import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent; -import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; -import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent; -import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; -import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredTextReParser; -import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; -import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection; -import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; -import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; -import org.eclipse.wst.sse.core.internal.util.Debug; -import org.eclipse.wst.sse.core.internal.util.Utilities; -import org.eclipse.wst.sse.core.utils.StringUtils; - - -/** - * This class provides a centralized place to put "reparsing" logic. This is - * the logic that reparses the text incrementally, as a user types in new - * characters, or DOM nodes are inserted or deleted. Note: it is not a thread - * safe class. - */ -public class StructuredDocumentReParser implements IStructuredTextReParser { - protected IStructuredDocumentRegion dirtyEnd = null; - protected IStructuredDocumentRegion dirtyStart = null; - final private String doubleQuote = new String(new char[]{'\"'}); - protected final CoreNodeList EMPTY_LIST = new CoreNodeList(); - protected String fChanges; - protected String fDeletedText; - protected boolean fIsEntireDocument; - - private FindReplaceDocumentAdapter fFindReplaceDocumentAdapter = null; - protected int fLengthDifference; - protected int fLengthToReplace; - protected Object fRequester; - protected int fStart; - // note: this is the impl class of IStructuredDocument, not the interface - // FUTURE_TO_DO: I believe some of these can be made private now.? - protected BasicStructuredDocument fStructuredDocument; - - /** - * variable used in anticiapation of multithreading - */ - protected boolean isParsing; - final private String singleQuote = new String(new char[]{'\''}); - - public StructuredDocumentReParser() { - super(); - } - - public StructuredDocumentEvent _checkBlockNodeList(List blockTagList) { - StructuredDocumentEvent result = null; - if (blockTagList != null) { - for (int i = 0; i < blockTagList.size(); i++) { - org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker blockTag = (org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker) blockTagList.get(i); - String tagName = blockTag.getTagName(); - result = checkForCriticalName("<" + tagName); //$NON-NLS-1$ - if (result != null) - break; - result = checkForCriticalName("</" + tagName); //$NON-NLS-1$ - if (result != null) - break; - } - } - return result; - } - - /** - * Common utility for checking for critical word such as " <SCRIPT>" - */ - private StructuredDocumentEvent _checkForCriticalWord(String criticalTarget, boolean checkEnd) { - StructuredDocumentEvent result = null; - int documentLength = fStructuredDocument.getLength(); - int propLen = fLengthToReplace; - if (propLen > documentLength) - propLen = documentLength; - int startNeighborhood = fStart - criticalTarget.length(); - int adjustInsert = 0; - if (startNeighborhood < 0) { - adjustInsert = 0 - startNeighborhood; - startNeighborhood = 0; - } - int endNeighborhood = fStart + fLengthToReplace + criticalTarget.length() - 1; - if (endNeighborhood > documentLength) - endNeighborhood = documentLength - 1; - int oldlen = endNeighborhood - startNeighborhood; // + 1; - if (oldlen + startNeighborhood > documentLength) { - oldlen = documentLength - startNeighborhood; - } - String oldText = fStructuredDocument.get(startNeighborhood, oldlen); - String peek = StringUtils.paste(oldText, fChanges, criticalTarget.length() - adjustInsert, fLengthToReplace); - boolean isCriticalString = checkTagNames(oldText, criticalTarget, checkEnd); - boolean toBeCriticalString = checkTagNames(peek, criticalTarget, checkEnd); - if ((isCriticalString != toBeCriticalString) || // OR if both are - // critical and there's - // a change in the end - // tag ('>') - ((isCriticalString && toBeCriticalString) && (changeInIsEndedState(oldText, peek)))) { - // if it involves a change of a critical string (making one where - // there wasn't, or removing - // one where there was one) then reparse everthing. - result = reparse(0, documentLength - 1); - } - return result; - } - - private int _computeStartOfDifferences(CoreNodeList oldNodes, CoreNodeList newNodes) { - int startOfDifferences = -1; - int newNodesLength = newNodes.getLength(); - boolean foundDifference = false; - boolean done = false; - // we'll control our loop based on the old List length - int oldNodesLength = oldNodes.getLength(); - // be sure to check 'done' first, so startOfDifferences isn't - // icremented if done is true - done : while ((!done) && (++startOfDifferences < oldNodesLength)) { - IStructuredDocumentRegion oldNode = oldNodes.item(startOfDifferences); - // this lessThanEffectedRegion is to check to be sure the node is - // infact a candidate - // to be considered as "old". This check is important for the case - // where some - // text is replaceing text that - // appears identical, but is a different instance. For example, if - // the text - // is <P><B></B></P> and <B></B> is inserted at postion 3, - // resulting in <P><B></B><B></B></P> - // we do not want the - // first <B> to be considered old ... it is the new one, the - // second - // <B> is the old one. - if (_lessThanEffectedRegion(oldNode)) { - // be sure to check that we have new nodes to compare against. - if (startOfDifferences > newNodesLength) { - foundDifference = false; - done = true; - continue done; - } else { - // - IStructuredDocumentRegion newNode = newNodes.item(startOfDifferences); - // note: shift is 0 while at beginning of list, before the - // insertion (or deletion) point. After that, it is - // fStart+fLengthDifference - if (!(oldNode.sameAs(newNode, 0))) { - foundDifference = true; - done = true; - continue done; - } else { // if they are equal, then we will be keeping the - // old one, so - // we need to be sure its parentDocument is set back - // to - // the right instance - oldNode.setParentDocument(fStructuredDocument); - } - } - } else { - // we didn't literally find a difference, but we count it as - // such by implication - foundDifference = true; - done = true; - continue done; - } - } - // if we literally found a difference, then all is ok and we can - // return - // it. - // if we did not literally find one, then we have to decide why. - if (!foundDifference) { - if (newNodesLength == oldNodesLength) { // then lists are - // identical - // (and may be of zero - // length) - startOfDifferences = -1; - } else { - if (newNodesLength > oldNodesLength) { // then lists are - // identical except for - // newNodes added - startOfDifferences = oldNodesLength; - } else { - if (newNodesLength < oldNodesLength) { // then lists are - // identical except - // for old Nodes - // deleted - startOfDifferences = newNodesLength; - } - } - } - } - return startOfDifferences; - } - - private int _computeStartOfDifferences(IStructuredDocumentRegion oldNodeParam, ITextRegionList oldRegions, IStructuredDocumentRegion newNodeParam, ITextRegionList newRegions) { - int startOfDifferences = -1; - int newRegionsLength = newRegions.size(); - boolean foundDifference = false; - boolean done = false; - // we'll control our loop based on the old List length - int oldRegionsLength = oldRegions.size(); - // be sure to check 'done' first, so startOfDifferences isn't - // icremented if done is true - done : while ((!done) && (++startOfDifferences < oldRegionsLength)) { - ITextRegion oldRegion = oldRegions.get(startOfDifferences); - // this lessThanEffectedRegion is to check to be sure the node is - // infact a candidate - // to be considered as "old". This check is important for the case - // where some - // text is replaceing text that - // appears identical, but is a different instance. For example, if - // the text - // is <P><B></B></P> and <B></B> is inserted at postion 3, - // resulting in <P><B></B><B></B></P> - // we do not want the - // first <B> to be considered old ... it is the new one, the - // second - // <B> is the old one. - if (_lessThanEffectedRegion(oldNodeParam, oldRegion)) { - // be sure to check that we have new nodes to compare against. - if (startOfDifferences > newRegionsLength) { - foundDifference = false; - done = true; - continue done; - } else { - // - ITextRegion newRegion = newRegions.get(startOfDifferences); - // note: shift is 0 while at beginning of list, before the - // insertion (or deletion) point. After that, it is - // fStart+fLengthDifference - if (!(oldNodeParam.sameAs(oldRegion, newNodeParam, newRegion, 0))) { - foundDifference = true; - done = true; - continue done; - } else { - // if they are equal, then we will be keeping the old - // one. - // unlike the flatnode case, there is no reason to - // update - // the textstore, since its the same text store in - // either case - // (since its the same flatnode) - //oldRegion.setTextStore(fStructuredDocument.parentDocument); - } - } - } else { - // we didn't literally find a difference, but we count it as - // such by implication - foundDifference = true; - done = true; - continue done; - } - } - // if we literally found a difference, then all is ok and we can - // return - // it. - // if we did not literally find one, then we have to decide why. - if (!foundDifference) { - if (newRegionsLength == oldRegionsLength) { // then lists are - // identical (and may - // be of zero length) - startOfDifferences = -1; - } else { - if (newRegionsLength > oldRegionsLength) { // then lists are - // identical except - // for newRegions - // added - startOfDifferences = oldRegionsLength; - } else { - if (newRegionsLength < oldRegionsLength) { // then lists - // are identical - // except for - // old Nodes - // deleted - startOfDifferences = newRegionsLength; - } - } - } - } - return startOfDifferences; - } - - /** - * Part 1 of 2 steps to do a core_reparse - * - * Parses a portion of the current text in the IStructuredDocument and - * returns the raw result - */ - private IStructuredDocumentRegion _core_reparse_text(int rescanStart, int rescanEnd) { - fStructuredDocument.resetParser(rescanStart, rescanEnd); - return fStructuredDocument.getParser().getDocumentRegions(); - } - - /** - * Part 2 of 2 steps to do a core_reparse - * - * Integrates a list of StructuredDocumentRegions based on the current - * text contents of the IStructuredDocument into the IStructuredDocument - * data structure - */ - private StructuredDocumentEvent _core_reparse_update_model(IStructuredDocumentRegion newNodesHead, int rescanStart, int rescanEnd, CoreNodeList oldNodes, boolean firstTime) { - StructuredDocumentEvent result = null; - CoreNodeList newNodes = null; - // rescan - newNodes = new CoreNodeList(newNodesHead); - // adjust our newNode chain so the offset positions match - // our text store (not the simple string of text reparsed) - StructuredDocumentRegionIterator.adjustStart(newNodesHead, rescanStart); - // initialize the parentDocument variable of each instance in the new - // chain - StructuredDocumentRegionIterator.setParentDocument(newNodesHead, fStructuredDocument); - // initialize the structuredDocument variable of each instance in the - // new chain - //StructuredDocumentRegionIterator.setStructuredDocument(newNodesHead, - // fStructuredDocument); - // - if (firstTime) { - fStructuredDocument.setCachedDocumentRegion(newNodesHead); - fStructuredDocument.initializeFirstAndLastDocumentRegion(); - // note: since we are inserting nodes, for the first time, there - // is - // no adjustments - // to downstream stuff necessary. - result = new StructuredDocumentRegionsReplacedEvent(fStructuredDocument, fRequester, oldNodes, newNodes, fChanges, fStart, fLengthToReplace, fIsEntireDocument); - } else { - // note: integrates changes into model as a side effect - result = minimumEvent(oldNodes, newNodes); - } - result.setDeletedText(fDeletedText); - return result; - } - - private CoreNodeList _formMinimumList(CoreNodeList flatnodes, int startOfDifferences, int endOfDifferences) { - CoreNodeList minimalNodes = null; - // if startOfDifferces is still its initial value, then we have an - // empty document - if (startOfDifferences == -1) { - minimalNodes = EMPTY_LIST; - } else { - // if we do not have any flatnode in our flatnode list, then - // simply - // return our standard empty list - if (flatnodes.getLength() == 0) { - minimalNodes = EMPTY_LIST; - } else { - // if startOfDifferences is greater than endOfDifferences, - // then - // that means the calculations "crossed" each other, and - // hence, - // there really is no differences, so, again, return the empty - // list - if (startOfDifferences > endOfDifferences) { - minimalNodes = EMPTY_LIST; - } else { - // the last check be sure we have some differnces - if ((endOfDifferences > -1)) { - minimalNodes = new CoreNodeList(flatnodes.item(startOfDifferences), flatnodes.item(endOfDifferences)); - } else { - // there were no differences, the list wasn't - // minimized, so simply return it. - minimalNodes = flatnodes; - } - } - } - } - return minimalNodes; - } - - private boolean _greaterThanEffectedRegion(IStructuredDocumentRegion oldNode) { - boolean result = false; - int nodeStart = oldNode.getStartOffset(); - int changedRegionEnd = fStart + fLengthToReplace - 1; - result = nodeStart > changedRegionEnd; - return result; - } - - private boolean _greaterThanEffectedRegion(IStructuredDocumentRegion oldNode, ITextRegion oldRegion) { - boolean result = false; - int regionStartOffset = oldNode.getStartOffset(oldRegion); - int effectedRegionEnd = fStart + fLengthToReplace - 1; - result = regionStartOffset > effectedRegionEnd; - return result; - } - - private boolean _lessThanEffectedRegion(IStructuredDocumentRegion oldNode) { - boolean result = false; - int nodeEnd = oldNode.getEndOffset() - 1; - result = nodeEnd < fStart; - return result; - } - - private boolean _lessThanEffectedRegion(IStructuredDocumentRegion oldNode, ITextRegion oldRegion) { - boolean result = false; - int nodeEnd = oldNode.getEndOffset(oldRegion) - 1; - result = nodeEnd < fStart; - return result; - } - - private boolean _regionsSameKind(ITextRegion newRegion, ITextRegion oldRegion) { - boolean result = false; - // if one region is a container region, and the other not, always - // return false - // else, just check their type. - // DW druing refactoring, looks like a "typo" here, using 'old' in - // both. - // if (isContainerRegion(oldRegion) != isContainerRegion(oldRegion)) - if (isCollectionRegion(oldRegion) != isCollectionRegion(newRegion)) - result = false; - else if (oldRegion.getType().equals(newRegion.getType())) - result = true; - return result; - } - - // private boolean hasCollectionRegions(ITextRegion aRegion) { - // boolean result = false; - // if (aRegion instanceof ITextRegionCollection) { - // ITextRegionCollection regionContainter = (ITextRegionCollection) - // aRegion; - // ITextRegionList regions = regionContainter.getRegions(); - // Iterator iterator = regions.iterator(); - // while (iterator.hasNext()) { - // if (aRegion instanceof ITextRegionCollection) { - // result = true; - // break; - // } - // } - // } - // return result; - // } - /** - * This method is specifically to detect changes in 'isEnded' state, - * although it still does so with heuristics. If number of '>' changes, - * assume the isEnded state has changed. - */ - private boolean changeInIsEndedState(String oldText, String newText) { - int nOld = StringUtils.occurrencesOf(oldText, '>'); - int nNew = StringUtils.occurrencesOf(newText, '>'); - return !(nOld == nNew); - } - - private void checkAndAssignParent(IStructuredDocumentRegion oldNode, ITextRegion region) { - if (region instanceof ITextRegionContainer) { - ((ITextRegionContainer) region).setParent(oldNode); - return; - } - if (region instanceof ITextRegionCollection) { - ITextRegionCollection textRegionCollection = (ITextRegionCollection) region; - ITextRegionList regionList = textRegionCollection.getRegions(); - for (int i = 0; i < regionList.size(); i++) { - ITextRegion innerRegion = regionList.get(i); - checkAndAssignParent(oldNode, innerRegion); - } - } - } - - /** - * A change to a CDATA tag can result in all being reparsed. - */ - private StructuredDocumentEvent checkForCDATA() { - StructuredDocumentEvent result = null; - result = checkForCriticalKey("<![CDATA["); //$NON-NLS-1$ - if (result == null) - result = checkForCriticalKey("]]>"); //$NON-NLS-1$ - return result; - } - - /** - * If a comment start or end tag is being added or deleted, we'll rescan - * the whole document. The reason is that content that is revealed or - * commented out can effect the interpretation of the rest of the - * document. Note: for now this is very XML specific, can refactor/improve - * later. - */ - protected StructuredDocumentEvent checkForComments() { - StructuredDocumentEvent result = null; - result = checkForCriticalKey("<!--"); //$NON-NLS-1$ - if (result == null) - result = checkForCriticalKey("-->"); //$NON-NLS-1$ - // we'll also check for these degenerate cases - if (result == null) - result = checkForCriticalKey("<!--->"); //$NON-NLS-1$ - return result; - } - - /** - * Common utility for checking for critical word such as " <SCRIPT>" - */ - protected StructuredDocumentEvent checkForCriticalKey(String criticalTarget) { - return _checkForCriticalWord(criticalTarget, false); - } - - /** - * Common utility for checking for critical word such as " <SCRIPT>" - */ - private StructuredDocumentEvent checkForCriticalName(String criticalTarget) { - return _checkForCriticalWord(criticalTarget, true); - } - - // /** - // * Currently this method is pretty specific to ?ML - // * @deprecated - not really deprecated, but plan to make - // * protected ... I'm not sure why its public or misspelled? - // */ - protected StructuredDocumentEvent checkForCrossStructuredDocumentRegionBoundryCases() { - StructuredDocumentEvent result = null; - // Case 1: See if the language's syntax requires that multiple - // StructuredDocumentRegions be rescanned - if (result == null) { - result = checkForCrossStructuredDocumentRegionSyntax(); - } - // Case 2: "block tags" whose content is left unparsed - if (result == null) { - Object parser = fStructuredDocument.getParser(); - if (parser instanceof BlockTagParser) { - List blockTags = ((BlockTagParser) parser).getBlockMarkers(); - result = _checkBlockNodeList(blockTags); - } - } - // FUTURE_TO_DO: is there a better place to do this? - // or! do we already do it some other more central place? - if (result != null) { - result.setDeletedText(fDeletedText); - } - return result; - } - - /** - * Allow a reparser to check for extra syntactic cases that require - * parsing beyond the flatNode boundary. - * - * This implementation is very XML-centric. - */ - protected StructuredDocumentEvent checkForCrossStructuredDocumentRegionSyntax() { - StructuredDocumentEvent result; - // Case 1: Quote characters are involved - result = checkForQuotes(); - if (result == null) { - // Case 2: The input forms or undoes a comment beginning or - // comment - // end - result = checkForComments(); - } - if (result == null) { - // Case 3: The input forms or undoes a processing instruction - result = checkForPI(); - } - if (result == null) { - // Case 4: The input forms or undoes a CDATA section - result = checkForCDATA(); - } - return result; - } - - /** - * Checks to see if change request exactly matches the text it would be - * replacing. (In future, this, or similar method is where to check for - * "read only" attempted change.) - */ - private StructuredDocumentEvent checkForNoChange() { - StructuredDocumentEvent result = null; - // don't check equals unless lengths match - // should be a tiny bit faster, since usually not - // of equal lengths (I'm surprised String's equals method - // doesn't do this.) - if ((fChanges != null) && (fDeletedText != null) && (fChanges.length() == fDeletedText.length()) && (fChanges.equals(fDeletedText))) { - result = new NoChangeEvent(fStructuredDocument, fRequester, fChanges, fStart, fLengthToReplace); - ((NoChangeEvent)result).reason = NoChangeEvent.NO_CONTENT_CHANGE; - } - return result; - } - - /** - * A change to a PI tag can result in all being reparsed. - */ - private StructuredDocumentEvent checkForPI() { - StructuredDocumentEvent result = null; - result = checkForCriticalKey("<?"); //$NON-NLS-1$ - if (result == null) - result = checkForCriticalKey("?>"); //$NON-NLS-1$ - return result; - } - - // For simplicity, if either text to be deleted, or text to be inserted - // contains at least - // one quote, we'll search for previous quote in document, if any, and use - // that flatnode as - // a dirty start, and we'll use end of document as dirty end. We need to - // assume either \" or - // \' is an exceptable quote. (NOTE: this is, loosely, an XML assumption - // -- - // other languages - // would differ, but we'll "hard code" for XML for now. - // future_TODO: this is a really bad heuristic ... we should be looking - // for - // odd number of quotes - // within a structuredDocumentRegion (or something!) This causes way too - // much reparsing on - // simple cases, like deleting a tag with a quoted attribute! - private StructuredDocumentEvent checkForQuotes() { - // routine is supported with null or empty string meaning the same - // thing: deletion - if (fChanges == null) - fChanges = ""; //$NON-NLS-1$ - // - StructuredDocumentEvent result = null; - try { - int dirtyStartPos = -1; - String proposedDeletion = fStructuredDocument.get(fStart, fLengthToReplace); - if (fStart < fStructuredDocument.getLength()) { - if ((fChanges.indexOf(singleQuote) > -1) || (proposedDeletion.indexOf(singleQuote) > -1)) { - IRegion singleQuoteRegion = getFindReplaceDocumentAdapter().find(fStart, singleQuote, false, false, false, false); - if (singleQuoteRegion != null) { - dirtyStartPos = singleQuoteRegion.getOffset(); - } - } else if ((fChanges.indexOf(doubleQuote) > -1) || (proposedDeletion.indexOf(doubleQuote) > -1)) { - IRegion doubleQuoteRegion = getFindReplaceDocumentAdapter().find(fStart, doubleQuote, false, false, false, false); - if (doubleQuoteRegion != null) { - dirtyStartPos = doubleQuoteRegion.getOffset(); - } - } - } - if (dirtyStartPos > -1) { - // then we found one, do create new structuredDocument event - // based on the previous quote to end of document - // except, we need to be positive that the previous quote is - // in a "safe start" region (e.g. if in JSP content, we need - // to - // backup till we include the whole JSP region, in order for - // it - // to be correctly re-parsed. The backing up is done in the - // reparse/find dirty start from hint - // method. - result = reparse(dirtyStartPos, fStructuredDocument.getLength() - 1); - } - } catch (BadLocationException e) { - Logger.logException(e); - } - if (result != null) { - result.setDeletedText(fDeletedText); - } - return result; - } - - private StructuredDocumentEvent checkHeuristics() { - StructuredDocumentEvent result = null; - result = checkForNoChange(); - if (result == null) { - result = checkForCrossStructuredDocumentRegionBoundryCases(); - if (result == null) { - result = quickCheck(); - } - } - return result; - } - - /** - * Takes into account "tag name" rules for comparisons; case-insensitive. - */ - private boolean checkTagNames(String compareText, String criticalTarget, boolean checkEnd) { - boolean result = false; - if ((compareText == null) || (criticalTarget == null)) - return false; - int posOfCriticalWord = compareText.toLowerCase().indexOf(criticalTarget.toLowerCase()); - result = posOfCriticalWord > -1; - if (checkEnd && result) { - // instead of returning true right away, we'll only return true - // the - // potentially matched tag is indeed a tag, for example, if - // <SCRIPT - // becomes <SCRIPTS we don't want to say the latter is a critical - // tag - int lastPos = posOfCriticalWord + criticalTarget.length(); - if (lastPos < compareText.length()) { - char lastChar = compareText.charAt(lastPos); - // Future: check formal definition of this java method, vs. - // XML - // parsing rules - result = (!Character.isLetterOrDigit(lastChar)); - } - } - return result; - } - - /** - * The core reparsing method ... after the dirty start and dirty end have - * been calculated elsewhere, and the text updated. - */ - protected StructuredDocumentEvent core_reparse(int rescanStart, int rescanEnd, CoreNodeList oldNodes, boolean firstTime) { - IStructuredDocumentRegion newNodesHead = null; - StructuredDocumentEvent result = null; - newNodesHead = _core_reparse_text(rescanStart, rescanEnd); - result = _core_reparse_update_model(newNodesHead, rescanStart, rescanEnd, oldNodes, firstTime); - return result; - } - - /** - * Resets state to "not parsing" - */ - private synchronized void endReParse() { - isParsing = false; - dirtyStart = null; - dirtyEnd = null; - fChanges = null; - fDeletedText = null; - fIsEntireDocument = false; - } - - protected IStructuredDocumentRegion findDirtyEnd(int end) { - // Caution: here's one place we have to cast - IStructuredDocumentRegion result = fStructuredDocument.getRegionAtCharacterOffset(end); - // if not well formed, get one past, if there is something there - if ((result != null) && (!result.isEnded())) { - if (result.getNext() != null) { - result = result.getNext(); - } - } - // also, get one past if exactly equal to the end (this was needed - // as a simple fix to when a whole exact region is deleted. - // there's probably a better way. - if ((result != null) && (end == result.getEnd())) { - if (result.getNext() != null) { - result = result.getNext(); - } - } - // moved to subclass for quick transition - // 12/6/2001 - Since we've changed the parser/scanner to allow a lone - // '<' without - // always interpretting it as start of a tag name, we need to be a - // little fancier, in order - // to "skip" over any plain 'ol content between the lone '<' and any - // potential meating - // regions past plain 'ol content. - // if (isLoneOpenFollowedByContent(result) && (result.getNext() != - // null)) { - // result = result.getNext(); - // } - if (result != null) - fStructuredDocument.setCachedDocumentRegion(result); - dirtyEnd = result; - return dirtyEnd; - } - - protected void findDirtyStart(int start) { - IStructuredDocumentRegion result = fStructuredDocument.getRegionAtCharacterOffset(start); - // heuristic: if the postion is exactly equal to the start, then - // go back one more, if it exists. This prevents problems with - // insertions - // of text that should be merged with the previous node instead of - // simply hung - // off of it as a separate node (ex.: XML content inserted right - // before - // an open - // bracket should become part of the previous content node) - if (result != null) { - IStructuredDocumentRegion previous = result.getPrevious(); - if ((previous != null) && ((!(previous.isEnded())) || (start == result.getStart()))) { - result = previous; - } - // If we are now at the end of a "tag dependent" content area (or - // JSP area) - // then we need to back up all the way to the beginning of that. - IStructuredDocumentRegion potential = result; - // moved to subclass to speed transition - // while (isPartOfBlockRegion(potential)) { - // potential = potential.getPrevious(); - // } - if (potential != null) { - result = potential; - fStructuredDocument.setCachedDocumentRegion(result); - } - } - dirtyStart = result; - } - - protected CoreNodeList formOldNodes(IStructuredDocumentRegion dirtyStart, IStructuredDocumentRegion dirtyEnd) { - CoreNodeList oldNodes = new CoreNodeList(dirtyStart, dirtyEnd); - // Now save the old text, that "goes with" the old nodes and regions. - // Notice we are getting it directly from the text store - String oldText = null; - int oldStart = -1; - int oldEnd = -1; - // make sure there is some text, if not, use empty string - // (if one node is not null, the other should ALWAYS be not null too, - // since it - // would at least be equal to it.) - if (dirtyStart != null) { - oldStart = dirtyStart.getStart(); - oldEnd = dirtyEnd.getEnd(); - oldText = fStructuredDocument.get(oldStart, oldEnd - oldStart); - } else { - oldStart = 0; - oldEnd = 0; - oldText = ""; //$NON-NLS-1$ - } - // create a temporary text store for this text - SubSetTextStore subTextStore = new SubSetTextStore(oldText, oldStart, oldEnd, fStructuredDocument.getLength()); - // Now update the text store of the oldNodes - StructuredDocumentRegionIterator.setParentDocument(oldNodes, new MinimalDocument(subTextStore)); - return oldNodes; - } - - /** - * @return Returns the findReplaceDocumentAdapter. - */ - public FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() { - if (fFindReplaceDocumentAdapter == null) { - fFindReplaceDocumentAdapter = new FindReplaceDocumentAdapter(fStructuredDocument); - } - return fFindReplaceDocumentAdapter; - } - - // Note: if thead safety is needed, this and all the other public methods - // of this class - // should be synchronized. - public void initialize(Object requester, int start, int lengthToReplace, String changes) { - isParsing = true; - fRequester = requester; - fStart = start; - fLengthToReplace = lengthToReplace; - fChanges = changes; - // notice this one is derived - fLengthDifference = Utilities.calculateLengthDifference(fChanges, fLengthToReplace); - fDeletedText = fStructuredDocument.get(fStart, fLengthToReplace); - int docLength = fStructuredDocument.getLength(); - fIsEntireDocument = lengthToReplace >= docLength && docLength > 0; - } - - protected void insertNodes(IStructuredDocumentRegion previousOldNode, IStructuredDocumentRegion nextOldNode, CoreNodeList newNodes) { - // - IStructuredDocumentRegion firstNew = null; - IStructuredDocumentRegion lastNew = null; - // - IStructuredDocumentRegion oldPrevious = previousOldNode; - IStructuredDocumentRegion oldNext = nextOldNode; - // - if (newNodes.getLength() > 0) { - // get pointers - firstNew = newNodes.item(0); - lastNew = newNodes.item(newNodes.getLength() - 1); - // switch surrounding StructuredDocumentRegions' references to - // lists - if (oldPrevious != null) - oldPrevious.setNext(firstNew); - if (oldNext != null) { - oldNext.setPrevious(lastNew); - } else { - // SIDE EFFECT - // if oldNext is null, that means we are replaceing the - // lastNode in the chain, - // so we need to update the structuredDocuments lastNode as - // the - // last of the new nodes. - fStructuredDocument.setLastDocumentRegion(newNodes.item(newNodes.getLength() - 1)); - } - if (firstNew != null) - firstNew.setPrevious(oldPrevious); - if (lastNew != null) - lastNew.setNext(oldNext); - } - // else nothing to insert - } - - /** - * @param oldRegion - */ - private boolean isCollectionRegion(ITextRegion aRegion) { - return (aRegion instanceof ITextRegionCollection); - } - - /** - * @return boolean - */ - public boolean isParsing() { - return isParsing; - } - - /** - * The minimization algorithm simply checks the old nodes to see if any of - * them "survived" the rescan and are unchanged. If so, the instance of - * the old node is used instead of the new node. Before the requested - * change, need to check type, offsets, and text to determine if the same. - * After the requested change, need to check type and text, but adjust the - * offsets to what ever the change was. - */ - protected StructuredDocumentEvent minimumEvent(CoreNodeList oldNodes, CoreNodeList newNodes) { - StructuredDocumentEvent event = null; - CoreNodeList minimalOldNodes = null; - CoreNodeList minimalNewNodes = null; - // To minimize nodes, we'll collect all those - // that are not equal into old and new lists - // Note: we assume that old and new nodes - // are basically contiguous -- and we force it to be so, - // by starting at the beginning to - // find first difference, and then starting at the end to find - // last difference. Everything in between we assume is different. - // - // - // - // startOfDifferences is the index into the core node list where the - // first difference - // occurs. But it may point into the old or the new list. - int startOfDifferences = _computeStartOfDifferences(oldNodes, newNodes); - int endOfDifferencesOld = -1; - int endOfDifferencesNew = -1; - // if one of the lists are shorter than where the differences start, - // then - // then some portion of the lists are identical - if ((startOfDifferences >= oldNodes.getLength()) || (startOfDifferences >= newNodes.getLength())) { - if (oldNodes.getLength() < newNodes.getLength()) { - // Then there are new regions to add - // these lengths will cause the vector of old ones to not - // have any elements, and the vector of new regions to have - // just the new ones not in common with the old ones - //startOfDifferences should equal oldNodes.getLength(), - // calculated above on _computeStartOfDifferences - minimalOldNodes = EMPTY_LIST; - endOfDifferencesNew = newNodes.getLength() - 1; - minimalNewNodes = _formMinimumList(newNodes, startOfDifferences, endOfDifferencesNew); - } else { - if (oldNodes.getLength() > newNodes.getLength()) { - // delete old - // then there are old regions to delete - // these lengths will cause the vector of old regions to - // contain the ones to delete, and the vector of new - // regions - // not have any elements - //startOfDifferences should equal newNodes.getLength(), - // calculated above on _computeStartOfDifferences - endOfDifferencesOld = oldNodes.getLength() - 1; - minimalOldNodes = _formMinimumList(oldNodes, startOfDifferences, endOfDifferencesOld); - minimalNewNodes = EMPTY_LIST; - } else - // unlikely event - event = new NoChangeEvent(fStructuredDocument, fRequester, fChanges, fStart, fLengthToReplace); - } - } else { - // We found a normal startOfDiffernces, but have not yet found the - // ends. - // We'll look for the end of differences by going backwards down - // the two lists. - // Here we need a seperate index for each array, since they may be - // (and - // probably are) of different lengths. - int indexOld = oldNodes.getLength() - 1; - int indexNew = newNodes.getLength() - 1; - // The greaterThanEffectedRegion is important to gaurd against - // incorrect counting - // when something identical is inserted to what's already there - // (see minimization test case 5) - // Note: the indexOld > startOfDifferences keeps indexOld from - // getting too small, - // so that the subsequent oldNodes.item(indexOld) is always valid. - while ((indexOld >= startOfDifferences) && (_greaterThanEffectedRegion(oldNodes.item(indexOld)))) { - if (!(oldNodes.item(indexOld).sameAs(newNodes.item(indexNew), fLengthDifference))) { - break; - } else { - // if they are equal, then we will be keeping the old one, - // so - // we need to be sure its parentDocument is set back to - // the - // right instance - oldNodes.item(indexOld).setParentDocument(fStructuredDocument); - } - indexOld--; - indexNew--; - } - endOfDifferencesOld = indexOld; - endOfDifferencesNew = indexNew; - minimalOldNodes = _formMinimumList(oldNodes, startOfDifferences, endOfDifferencesOld); - minimalNewNodes = _formMinimumList(newNodes, startOfDifferences, endOfDifferencesNew); - } ///////////////////////////////////////// - // - IStructuredDocumentRegion firstDownStreamNode = null; - event = regionCheck(minimalOldNodes, minimalNewNodes); - if (event != null) { - firstDownStreamNode = minimalOldNodes.item(0).getNext(); - if (firstDownStreamNode != null && fLengthDifference != 0) { // if - // firstDownStream - // is - // null, - // then - // we're - // at - // the - // end - // of - // the - // document - StructuredDocumentRegionIterator.adjustStart(firstDownStreamNode, fLengthDifference); - } // - } else { - event = nodesReplacedCheck(minimalOldNodes, minimalNewNodes); - // now splice the new chain of nodes to where the old chain is (or - // was) - // the firstDownStreamNode (the first of those after the new - // nodes) - // is - // remembered as a tiny optimization. - if (minimalOldNodes.getLength() == 0 && minimalNewNodes.getLength() > 0) { - // if no old nodes are being deleted, then use the - // the newNodes offset (minus one) to find the point to - // update downstream nodes, and after updating downstream - // nodes postions, insert the new ones. - int insertOffset = minimalNewNodes.item(0).getStartOffset(); - IStructuredDocumentRegion lastOldUnchangedNode = null; - if (insertOffset > 0) { - lastOldUnchangedNode = fStructuredDocument.getRegionAtCharacterOffset(insertOffset - 1); - firstDownStreamNode = lastOldUnchangedNode.getNext(); - } else { - // we're inserting at very beginning - firstDownStreamNode = fStructuredDocument.getFirstStructuredDocumentRegion(); - // SIDE EFFECT: change the firstNode pointer if we're - // inserting at beginning - fStructuredDocument.setFirstDocumentRegion(minimalNewNodes.item(0)); - } - StructuredDocumentRegionIterator.adjustStart(firstDownStreamNode, fLengthDifference); - insertNodes(lastOldUnchangedNode, firstDownStreamNode, minimalNewNodes); - // this (nodes replaced) is the only case where we need to - // update the cached Node - reSetCachedNode(minimalOldNodes, minimalNewNodes); - } else { - firstDownStreamNode = switchNodeLists(minimalOldNodes, minimalNewNodes); - // no need to adjust the length of the new nodes themselves, - // they - // are already correct, but we do need to - // adjust all "down stream" nodes with the length of the - // insertion or deletion - // --- adjustment moved to calling method. - if (firstDownStreamNode != null) { - // && event != null - StructuredDocumentRegionIterator.adjustStart(firstDownStreamNode, fLengthDifference); - } // - // this (nodes replaced) is the only case where we need to - // update the cached Node - reSetCachedNode(minimalOldNodes, minimalNewNodes); - } - } - return event; - } - - // TODO: This should be abstract. - public IStructuredTextReParser newInstance() { - return new StructuredDocumentReParser(); - } - - protected StructuredDocumentEvent nodesReplacedCheck(CoreNodeList oldNodes, CoreNodeList newNodes) { - // actually, nothing to check here, since (and assuming) we've already - // minimized the number of nodes, and ruled out mere region changes - StructuredDocumentEvent result = new StructuredDocumentRegionsReplacedEvent(fStructuredDocument, fRequester, oldNodes, newNodes, fChanges, fStart, fLengthToReplace, fIsEntireDocument); - return result; - } - - /** - * A method to allow any heuristic "quick checks" that might cover many - * many cases, before expending the time on a full reparse. - * - */ - public StructuredDocumentEvent quickCheck() { - StructuredDocumentEvent result = null; - // if the dirty start is null, then we have an empty document. - // in which case we'll return null so everything can be - // reparsed "from scratch" . If its not null, we'll give the flatnode - // a - // chance - // to handle, but only if there is one flatnode involved. - if (dirtyStart != null && dirtyStart == dirtyEnd) { - IStructuredDocumentRegion targetNode = dirtyStart; - result = dirtyStart.updateRegion(fRequester, targetNode, fChanges, fStart, fLengthToReplace); - if (result != null) { - // at this point only, we need to update the text store and - // and downstream nodes. - // FUTURE_TO_DO: can this dependency on structuredDocument - // method be eliminated? - fStructuredDocument.updateDocumentData(fStart, fLengthToReplace, fChanges); - IStructuredDocumentRegion firstDownStreamNode = targetNode.getNext(); - // then flatnode must have been the last one, so need to - // update - // any downstream ones - if (firstDownStreamNode != null) { - StructuredDocumentRegionIterator.adjustStart(firstDownStreamNode, fLengthDifference); - } - } - } - if (result != null) { - result.setDeletedText(fDeletedText); - } - return result; - } - - /** - * If only one node is involved, sees how many regions are changed. If - * only one, then its a 'regionChanged' event ... if more than one, its a - * 'regionsReplaced' event. - */ - protected StructuredDocumentEvent regionCheck(CoreNodeList oldNodes, CoreNodeList newNodes) { - if (Debug.debugStructuredDocument) - System.out.println("IStructuredDocument::regionsReplacedCheck"); //$NON-NLS-1$ - //$NON-NLS-1$ - //$NON-NLS-1$ - // the "regionsReplaced" event could only be true if and only if the - // nodelists - // are each only "1" in length. - StructuredDocumentEvent result = null; - int oldLength = oldNodes.getLength(); - int newLength = newNodes.getLength(); - if ((oldLength != 1) || (newLength != 1)) { - result = null; - } else { - IStructuredDocumentRegion oldNode = oldNodes.item(0); - IStructuredDocumentRegion newNode = newNodes.item(0); - result = regionCheck(oldNode, newNode); - } - return result; - } - - /** - * If only one node is involved, sees how many regions are changed. If - * only one, then its a 'regionChanged' event ... if more than one, its a - * 'regionsReplaced' event. - */ - protected StructuredDocumentEvent regionCheck(IStructuredDocumentRegion oldNode, IStructuredDocumentRegion newNode) { - // - StructuredDocumentEvent result = null; - ITextRegionList oldRegions = oldNode.getRegions(); - ITextRegionList newRegions = newNode.getRegions(); - ITextRegion[] oldRegionsArray = oldRegions.toArray(); - ITextRegion[] newRegionsArray = newRegions.toArray(); - // - // for the 'regionsReplaced' event, we don't care if - // the regions changed due to type, or text, - // we'll just collect all those that are not equal - // into the old and new region lists. - // Note: we, of course, assume that old and new regions - // are basically contiguous -- and we force it to be so, - // even if not literally so, by starting at beginning to - // find first difference, and then starting at end to find - // last difference. Everything in between we assume is different. - // - // going up is easy, we start at zero in each, and continue - // till regions are not the same. - int startOfDifferences = _computeStartOfDifferences(oldNode, oldRegions, newNode, newRegions); - int endOfDifferencesOld = -1; - int endOfDifferencesNew = -1; - // - // - // if one of the lists are shorter than where the differences start, - // then - // then some portion of the lists are identical - if ((startOfDifferences >= oldRegions.size()) || (startOfDifferences >= newRegions.size())) { - if (oldRegions.size() < newRegions.size()) { - // INSERT CASE - // then there are new regions to add - // these lengths will cause the vector of old ones to not - // have any elements, and the vector of new regions to have - // just the new ones. - startOfDifferences = oldRegionsArray.length; - endOfDifferencesOld = oldRegionsArray.length - 1; - endOfDifferencesNew = newRegionsArray.length - 1; - } else { - if (oldRegions.size() > newRegions.size()) { - // DELETE CASE - // then there are old regions to delete - // these lengths will cause the vector of old regions to - // contain the ones to delete, and the vector of new - // regions - // not have any elements - startOfDifferences = newRegionsArray.length; - endOfDifferencesOld = oldRegionsArray.length - 1; - endOfDifferencesNew = newRegionsArray.length - 1; - } else { - // else the lists are identical! - // unlikely event, probably error in current design, since - // we check for identity at the very beginning of - // reparsing. - result = new NoChangeEvent(fStructuredDocument, fRequester, fChanges, fStart, fLengthToReplace); - } - } - } else { - if ((startOfDifferences > -1) && (endOfDifferencesOld < 0) && (endOfDifferencesNew < 0)) { - // We found a normal startOfDiffernces, but have not yet found - // the ends. - // We'll look for the end of differences by going backwards - // down the two lists. - // Here we need a seperate index for each array, since they - // may - // be (and - // probably are) of different lengths. - int indexOld = oldRegionsArray.length - 1; - int indexNew = newRegionsArray.length - 1; - while ((indexOld >= startOfDifferences) && (_greaterThanEffectedRegion(oldNode, oldRegionsArray[indexOld]))) { - if ((!(oldNode.sameAs(oldRegionsArray[indexOld], newNode, newRegionsArray[indexNew], fLengthDifference)))) { - //endOfDifferencesOld = indexOne; - //endOfDifferencesNew = indexTwo; - break; - } - indexOld--; - indexNew--; - } - endOfDifferencesOld = indexOld; - endOfDifferencesNew = indexNew; - } - } - // - // result != null means the impossible case above occurred - if (result == null) { - // Now form the two vectors of different regions - ITextRegionList holdOldRegions = new TextRegionListImpl(); - ITextRegionList holdNewRegions = new TextRegionListImpl(); - if (startOfDifferences > -1 && endOfDifferencesOld > -1) { - for (int i = startOfDifferences; i <= endOfDifferencesOld; i++) { - holdOldRegions.add(oldRegionsArray[i]); - } - } - if (startOfDifferences > -1 && endOfDifferencesNew > -1) { - for (int i = startOfDifferences; i <= endOfDifferencesNew; i++) { - holdNewRegions.add(newRegionsArray[i]); - } - } - if (holdOldRegions.size() == 0 && holdNewRegions.size() == 0) { - // then this means the regions were identical, which means - // someone - // pasted exactly the same thing they had selected, or !!! - // someone deleted the end bracket of the tag. !!!? - result = new NoChangeEvent(fStructuredDocument, fRequester, fChanges, fStart, fLengthToReplace); - } else { - //If both holdOldRegions and holdNewRegions are of length 1, - // then its - // a "region changed" event, else a "regions replaced" event. - // so we want the new instance of region to become part of the - // old instance of old node - if ((holdOldRegions.size() == 1) && (holdNewRegions.size() == 1) && _regionsSameKind((holdNewRegions.get(0)), (holdOldRegions.get(0)))) { - ITextRegion newOldRegion = swapNewForOldRegion(oldNode, holdOldRegions.get(0), newNode, holdNewRegions.get(0)); - // -- need to update any down stream regions, within this - // 'oldNode' - updateDownStreamRegions(oldNode, newOldRegion); - result = new RegionChangedEvent(fStructuredDocument, fRequester, oldNode, newOldRegion, fChanges, fStart, fLengthToReplace); - } else { - replaceRegions(oldNode, holdOldRegions, newNode, holdNewRegions); - // -- need to update any down stream regions, within this - // 'oldNode' - // don't need with the way replaceRegions is implemented. - // It handles. - //if(holdNewRegions.size() > 0) - //updateDownStreamRegions(oldNode, (ITextRegion) - // holdNewRegions.lastElement()); - result = new RegionsReplacedEvent(fStructuredDocument, fRequester, oldNode, holdOldRegions, holdNewRegions, fChanges, fStart, fLengthToReplace); - } - } - } - return result; - } - - /** - * An entry point for reparsing. It calculates the dirty start and dirty - * end flatnodes based on the start point and length of the changes. - * - */ - public StructuredDocumentEvent reparse() { - StructuredDocumentEvent result = null; - // if we do not have a cachedNode, then the document - // must be empty, so simply use 'null' as the dirtyStart and dirtyEnd - // otherwise, find them. - if (fStructuredDocument.getCachedDocumentRegion() != null) { - findDirtyStart(fStart); - int end = fStart + fLengthToReplace; - findDirtyEnd(end); - } - if (fStructuredDocument.getCachedDocumentRegion() != null) { - result = checkHeuristics(); - } - if (result == null) { - result = reparse(dirtyStart, dirtyEnd); - } - endReParse(); - return result; - } - - /** - * An entry point for reparsing. It calculates the dirty start and dirty - * end flatnodes based on suggested positions to begin and end. This is - * needed for cases where parsing must go beyond the immediate node and - * its direct neighbors. - * - */ - protected StructuredDocumentEvent reparse(int reScanStartHint, int reScanEndHint) { - StructuredDocumentEvent result = null; - // if we do not have a cachedNode, then the document - // must be empty, so simply use 'null' as the dirtyStart and dirtyEnd - if (fStructuredDocument.getCachedDocumentRegion() != null) { - findDirtyStart(reScanStartHint); - findDirtyEnd(reScanEndHint); - } - result = reparse(dirtyStart, dirtyEnd); - isParsing = false; - // debug - //verifyStructured(result); - return result; - } - - /** - * The core reparsing method ... after the dirty start and dirty end have - * been calculated elsewhere. - */ - protected StructuredDocumentEvent reparse(IStructuredDocumentRegion dirtyStart, IStructuredDocumentRegion dirtyEnd) { - StructuredDocumentEvent result = null; - int rescanStart = -1; - int rescanEnd = -1; - boolean firstTime = false; - // - // "save" the oldNodes (that may be replaced) in a list - CoreNodeList oldNodes = formOldNodes(dirtyStart, dirtyEnd); - if (dirtyStart == null || dirtyEnd == null) { - // dirtyStart or dirty end are null, then that means we didn't - // have - // a - // cached node, which means we have an empty document, so we - // just need to rescan the changes - rescanStart = 0; - rescanEnd = fChanges.length(); - firstTime = true; - } else { - // set the start of the text to rescan - rescanStart = dirtyStart.getStart(); - // - // set the end of the text to rescan - // notice we use the same rationale as for the rescanStart, - // with the added caveat that length has to be added to it, - // to compensate for the new text which has been added or deleted. - // If changes has zero length, then "length" will be negative, - // since - // we are deleting text. Otherwise, use the difference between - // what's selected to be replaced and the length of the new text. - rescanEnd = dirtyEnd.getEnd() + fLengthDifference; - } - // now that we have the old stuff "saved" away, update the document - // with the changes. - // FUTURE_TO_DO -- don't fire "document changed" event till later - fStructuredDocument.updateDocumentData(fStart, fLengthToReplace, fChanges); - // ------------------ now the real work - result = core_reparse(rescanStart, rescanEnd, oldNodes, firstTime); - // - // event is returned to the caller, incase there is - // some opitmization they can do - return result; - } - - protected void replaceRegions(IStructuredDocumentRegion oldNode, ITextRegionList oldRegions, IStructuredDocumentRegion newNode, ITextRegionList newRegions) { - int insertPos = -1; - ITextRegionList regions = oldNode.getRegions(); - // make a fake flatnode to be new parent of oldRegions, so their text - // will be right. - //IStructuredDocumentRegion holdOldStructuredDocumentRegion = new - // BasicStructuredDocumentRegion(oldNode); - // - // need to reset the parent of the new to-be-inserted regions to be - // the - // same oldNode that is the one having its regions changed - // DW, 4/16/2003, removed since ITextRegion no longer has parent. - // ITextRegionContainer oldParent = oldNode; - // for (int i = 0; i < newRegions.size(); i++) { - // AbstractRegion region = (AbstractRegion) newRegions.elementAt(i); - // region.setParent(oldParent); - // } - // if there are no old regions, insert the new regions according to - // offset - if (oldRegions.size() == 0) { - ITextRegion firstNewRegion = newRegions.get(0); - int firstOffset = newNode.getStartOffset(firstNewRegion); - // if at beginning, insert there - if (firstOffset == 0) { - insertPos = 0; - } else { - // - ITextRegion regionAtOffset = oldNode.getRegionAtCharacterOffset(firstOffset); - if (regionAtOffset == null) - insertPos = regions.size(); - else - insertPos = regions.indexOf(regionAtOffset); - } - } else { - // else, delete old ones before inserting new ones in their place - ITextRegion firstOldRegion = oldRegions.get(0); - insertPos = regions.indexOf(firstOldRegion); - regions.removeAll(oldRegions); - } - regions.addAll(insertPos, newRegions); - // now regions vector of each node should be of equal length, - // so go through each, and make sure the old regions - // offsets matches the new regions offsets - // (we'll just assign them all, but could be slightly more effiecient) - ITextRegionList allNewRegions = newNode.getRegions(); - for (int i = 0; i < regions.size(); i++) { - ITextRegion nextOldishRegion = regions.get(i); - ITextRegion nextNewRegion = allNewRegions.get(i); - nextOldishRegion.equatePositions(nextNewRegion); - checkAndAssignParent(oldNode, nextOldishRegion); - } - oldNode.setLength(newNode.getLength()); - oldNode.setEnded(newNode.isEnded()); - oldNode.setParentDocument(newNode.getParentDocument()); - // removed concept of part of these regions, so no longer need to do. - // for (int i = 0; i < oldRegions.size(); i++) { - // ITextRegion region = (ITextRegion) oldRegions.elementAt(i); - // region.setParent(holdOldStructuredDocumentRegion); - // } - } - - private void reSetCachedNode(CoreNodeList oldNodes, CoreNodeList newNodes) { - // use the last newNode as the new cachedNode postion, unless its null - // (e.g. when nodes are deleted) in which case, assign - // it to a "safe" node so we don't lose reference to the - // structuredDocument! - if (newNodes.getLength() > 0) { - // use last new node as the cache - fStructuredDocument.setCachedDocumentRegion(newNodes.item(newNodes.getLength() - 1)); - } else { - // if cachedNode is an old node, then we're in trouble: - // we can't leave it as the cached node! and its already - // been disconnected from the model, so we can't do getNext - // or getPrevious, so we'll get one that is right before - // (or right after) the offset of the old nodes that are being - // deleted. - // - // if newNodesHead and cachedNode are both null, then - // it means we were asked to insert an empty string into - // an empty document. So we have nothing to do here - // (that is, we have no node to cache) - // similarly if there are no new nodes and no old nodes then - // nothing to do (but that should never happen ... we shouldn't - // get there if there is no event to generate). - if ((fStructuredDocument.getCachedDocumentRegion() != null) && (oldNodes.getLength() > 0)) { - // note: we can't simple use nodeAtCharacterOffset, since it - // depends on cachedNode. - if (oldNodes.includes(fStructuredDocument.getCachedDocumentRegion())) - fStructuredDocument.setCachedDocumentRegion(fStructuredDocument.getFirstStructuredDocumentRegion()); - } - if ((fStructuredDocument.getCachedDocumentRegion() == null) && (Debug.displayWarnings)) { - // this will happen now legitamately when all text is deleted - // from a document - System.out.println("Warning: StructuredDocumentReParser::reSetCachedNode: could not find a node to cache! (its ok if all text deleted)"); //$NON-NLS-1$ - } - } - } - - public void setStructuredDocument(IStructuredDocument newStructuredDocument) { - // NOTE: this method (and class) depend on being able to - // do the following cast (i.e. references some fields directly) - fStructuredDocument = (BasicStructuredDocument) newStructuredDocument; - fFindReplaceDocumentAdapter = null; - } - - private IStructuredDocumentRegion splice(CoreNodeList oldNodes, CoreNodeList newNodes) { - // - IStructuredDocumentRegion firstOld = null; - IStructuredDocumentRegion firstNew = null; - IStructuredDocumentRegion lastOld = null; - IStructuredDocumentRegion lastNew = null; - // - IStructuredDocumentRegion oldPrevious = null; - IStructuredDocumentRegion oldNext = null; - IStructuredDocumentRegion newPrevious = null; - IStructuredDocumentRegion newNext = null; - // - // if called with both arguments empty lists, we can disregard. - // this happens, for example, when some text is replaced with the - // identical text. - if ((oldNodes.getLength() == 0) && (newNodes.getLength() == 0)) { - return null; - } - // get pointers - if (newNodes.getLength() > 0) { - firstNew = newNodes.item(0); - lastNew = newNodes.item(newNodes.getLength() - 1); - } - // - if (oldNodes.getLength() > 0) { - firstOld = oldNodes.item(0); - lastOld = oldNodes.item(oldNodes.getLength() - 1); - if (firstOld != null) - oldPrevious = firstOld.getPrevious(); - if (lastOld != null) - oldNext = lastOld.getNext(); - } - // handle switch - if (newNodes.getLength() > 0) { - // switch surrounding StructuredDocumentRegions' references to - // lists - if (oldPrevious != null) - oldPrevious.setNext(firstNew); - if (newPrevious != null) - newPrevious.setNext(firstOld); - if (oldNext != null) - oldNext.setPrevious(lastNew); - if (newNext != null) - newNext.setPrevious(lastOld); - // switch list pointers to surrounding StructuredDocumentRegions - if (firstOld != null) - firstOld.setPrevious(newPrevious); - if (lastOld != null) - lastOld.setNext(newNext); - if (firstNew != null) - firstNew.setPrevious(oldPrevious); - if (lastNew != null) - lastNew.setNext(oldNext); - } else { - // short circuit when there are no new nodes - if (oldPrevious != null) - oldPrevious.setNext(oldNext); - if (oldNext != null) - oldNext.setPrevious(oldPrevious); - } - // - // SIDE EFFECTs - // if we have oldNodes, and if oldNext or oldPrevious is null, - // that means we are replacing - // the lastNode or firstNode the structuredDocuments's chain of nodes, - // so we need to update the structuredDocuments last or first Node - // as the last or first of the new nodes. - // (and sometimes even these will be null! such as when deleting all - // text in a document). - if ((oldNext == null) && (oldNodes.getLength() > 0)) { - if (newNodes.getLength() > 0) { - fStructuredDocument.setLastDocumentRegion(lastNew); - } else { - // in this case, the last node is being deleted, but not - // replaced - // with anything. In this case, we can just back up one - // from the first old node - fStructuredDocument.setLastDocumentRegion(firstOld.getPrevious()); - } - } - if ((oldPrevious == null) && (oldNodes.getLength() > 0)) { - if (newNodes.getLength() > 0) { - fStructuredDocument.setFirstDocumentRegion(firstNew); - } else { - // in this case the first node is being deleted, but not - // replaced - // with anything. So, we just go one forward past the last old - // node. - fStructuredDocument.setFirstDocumentRegion(lastOld.getNext()); - } - } - // as a tiny optimization, we return the first of the downstream - // nodes, - // if any - return oldNext; - } - - /** - * The purpose of this method is to "reuse" the old container region, when - * found to be same (so same instance doesn't change). The goal is to - * "transform" the old region, so its equivelent to the newly parsed one. - * - */ - private ITextRegion swapNewForOldRegion(IStructuredDocumentRegion oldNode, ITextRegion oldRegion, IStructuredDocumentRegion newNode, ITextRegion newRegion) { - // makes the old region instance the correct size. - oldRegion.equatePositions(newRegion); - // adjusts old node instance appropriately - oldNode.setLength(newNode.getLength()); - oldNode.setEnded(newNode.isEnded()); - // we do have to set the parent document, since the oldNode's - // were set to a temporary one, then newNode's have the - // right one. - oldNode.setParentDocument(newNode.getParentDocument()); - // if we're transforming a container region, we need to be sure to - // transfer the new embedded regions, to the old parent - // Note: if oldRegion hasEmbeddedRegions, then we know the - // newRegion does too, since we got here because they were the - // same type. - if (isCollectionRegion(oldRegion)) { // || - // hasContainerRegions(oldRegion)) - // { - transferEmbeddedRegions(oldNode, (ITextRegionContainer) oldRegion, (ITextRegionContainer) newRegion); - } - return oldRegion; - } - - private IStructuredDocumentRegion switchNodeLists(CoreNodeList oldNodes, CoreNodeList newNodes) { - IStructuredDocumentRegion result = splice(oldNodes, newNodes); - // ensure that the old nodes hold no references to the existing model - if (oldNodes.getLength() > 0) { - IStructuredDocumentRegion firstItem = oldNodes.item(0); - firstItem.setPrevious(null); - IStructuredDocumentRegion lastItem = oldNodes.item(oldNodes.getLength() - 1); - lastItem.setNext(null); - } - return result; - } - - /** - * The purpose of this method is to "reuse" the old container region, when - * found to be same (so same instance doesn't change). The goal is to - * "transform" the old region, so its equivelent to the newly parsed one. - * - */ - private void transferEmbeddedRegions(IStructuredDocumentRegion oldNode, ITextRegionContainer oldRegion, ITextRegionContainer newRegion) { - // the oldRegion should already have the right parent, since - // we got here because all's equivelent except the region - // postions have changed. - //oldRegion.setParent(newRegion.getParent()); - // but we should check if there's "nested" embedded regions, and if - // so, we can just move them over. setting their parent as this old - // region. - ITextRegionList newRegionsToTransfer = newRegion.getRegions(); - oldRegion.setRegions(newRegionsToTransfer); - Iterator newRegionsInOldOne = newRegionsToTransfer.iterator(); - while (newRegionsInOldOne.hasNext()) { - ITextRegion newOne = (ITextRegion) newRegionsInOldOne.next(); - if (isCollectionRegion(newOne)) { // || - // hasContainerRegions(newOne)) { - //((ITextRegionContainer) newOne).setParent(oldRegion); - oldRegion.setRegions(newRegion.getRegions()); - } - } - } - - private void updateDownStreamRegions(IStructuredDocumentRegion flatNode, ITextRegion lastKnownRegion) { - // so all regions after the last known region (last known to be ok) - // have to have their start and end values adjusted. - ITextRegionList regions = flatNode.getRegions(); - int listLength = regions.size(); - int startIndex = 0; - // first, loop through to find where to start - for (int i = 0; i < listLength; i++) { - ITextRegion region = regions.get(i); - if (region == lastKnownRegion) { - startIndex = i; - break; - } - } - // now, beginning one past the last known one, loop - // through to end of list, adjusting the start and end postions. - startIndex++; - for (int j = startIndex; j < listLength; j++) { - ITextRegion region = regions.get(j); - region.adjustStart(fLengthDifference); - } - } -} |