/******************************************************************************* * Copyright (c) 2001, 2009 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 * *******************************************************************************/ package org.eclipse.wst.xml.core.internal.parser; import org.eclipse.jface.text.BadLocationException; import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; 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.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.text.TextRegionListImpl; import org.eclipse.wst.xml.core.internal.Logger; public class ContextRegionContainer implements ITextRegionContainer { protected int length; protected ITextRegionCollection parent; protected ITextRegionList regions; protected int start; protected int textLength; protected String type; public ContextRegionContainer() { super(); regions = new TextRegionListImpl(); } /** * these "deep" parenting is not normal, but just in case. */ private IStructuredDocument _getParentDocument() { // go up enough parents to get to document ITextRegionCollection parent = getParent(); while (!(parent instanceof IStructuredDocumentRegion)) { // would be an error not to be container, but // won't check for it now parent = ((ITextRegionContainer) parent).getParent(); } return ((IStructuredDocumentRegion) parent).getParentDocument(); } public void adjust(int i) { start += i; // I erroneously added length and textLength // TODO: may want to rename this method to adjustStart //length += i; //textLength += i; } public void adjustLength(int i) { length += i; } public void adjustStart(int i) { start += i; } public void adjustTextLength(int i) { textLength += i; } public boolean containsOffset(int i) { return getStartOffset() <= i && i < getEndOffset(); } public boolean containsOffset(ITextRegion containedRegion, int offset) { return getStartOffset(containedRegion) <= offset && offset < getEndOffset(containedRegion); } /** * This method is just to equate positions. clients may (will probably) * still need to make calls to equate regions, parent, etc. */ public void equatePositions(ITextRegion region) { start = region.getStart(); length = region.getLength(); textLength = region.getTextLength(); } public int getEnd() { return start + length; } public int getEndOffset() { // our startOffset take into account our parent, and our start return getStartOffset() + getLength(); } public int getEndOffset(ITextRegion containedRegion) { return getStartOffset(containedRegion) + containedRegion.getLength(); } public ITextRegion getFirstRegion() { return getRegions().get(0); } public String getFullText() { return getParent().getFullText(this); } public String getFullText(org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion aRegion) { // Must be proxied here since aRegion should always be a child of // *this* container and indexed from // this container's offset return parent.getFullText().substring(start + aRegion.getStart(), start + aRegion.getEnd()); } public ITextRegion getLastRegion() { return getRegions().get(getRegions().size() - 1); } public int getLength() { return length; } public int getNumberOfRegions() { return getRegions().size(); } public ITextRegionCollection getParent() { return parent; } /** * The parameter offset refers to the overall offset in the document. */ public ITextRegion getRegionAtCharacterOffset(int offset) { ITextRegion result = null; if (regions != null) { int thisStartOffset = getStartOffset(); if (offset < thisStartOffset) return null; int thisEndOffset = getStartOffset() + getLength(); if (offset > thisEndOffset) return null; // transform the requested offset to the "scale" that // regions are stored in, which are all relative to the // start point. //int transformedOffset = offset - getStartOffset(); // ITextRegionList regions = getRegions(); int length = regions.size(); int low = 0; int high = length; int mid = 0; // Binary search for the region while (low < high) { mid = low + ((high - low) >> 1); ITextRegion region = regions.get(mid); if (org.eclipse.wst.sse.core.internal.util.Debug.debugStructuredDocument) { System.out.println("region(s) in IStructuredDocumentRegion::getRegionAtCharacterOffset: " + region); //$NON-NLS-1$ System.out.println(" midpoint of search:" + mid); //$NON-NLS-1$ System.out.println(" requested offset: " + offset); //$NON-NLS-1$ //System.out.println(" transformedOffset: " + // transformedOffset); //$NON-NLS-1$ System.out.println(" region start: " + region.getStart()); //$NON-NLS-1$ System.out.println(" region end: " + region.getEnd()); //$NON-NLS-1$ System.out.println(" region type: " + region.getType()); //$NON-NLS-1$ System.out.println(" region class: " + region.getClass()); //$NON-NLS-1$ } // Region is before this one if (offset < region.getStart() + thisStartOffset) high = mid; else if (offset > (region.getEnd() + thisStartOffset - 1)) low = mid + 1; else return region; } return null; } return result; } public ITextRegionList getRegions() { return regions; } public int getStart() { return start; } public int getStartOffset() { return getParent().getStartOffset() + getStart(); } public int getStartOffset(ITextRegion containedRegion) { // it is an error to pass null to this method // ISSUE: need better "spec" on error behavior: // for now will return zero as this will roughly // work for some cases (and avoid NPE). if (containedRegion == null) { return getStartOffset(); } return getStartOffset() + containedRegion.getStart(); } /** * same as getFullText for this region type ... do we need to take white * space off? */ public String getText() { String result = null; try { IStructuredDocument parentDocument = _getParentDocument(); result = parentDocument.get(start, length); } catch (BadLocationException e) { Logger.logException("program error: unreachable exception", e); //$NON-NLS-1$ } return result; } public String getText(org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion aRegion) { // Must be proxied here since aRegion should always be a child of // *this* container and indexed from // this container's offset return parent.getText().substring(start + aRegion.getStart(), start + aRegion.getTextEnd()); } public int getTextEnd() { return start + textLength; } public int getTextEndOffset() { ITextRegion region = regions.get(regions.size() - 1); // our startOffset take into account our parent, and our start // (pa) 10/4 changed to be based on text end // it used to return incorrect value for embedded region containers // // TODO CRITICAL -- need to re-work this work around, so doesn't // depend on XMLRegionContext // // this is a workaround for 226823/////////// // for (int i = regions.size() - 1; i >= 0 && region.getType() == // XMLRegionContext.WHITE_SPACE; i--) // region = (ITextRegion) regions.get(i); // ///////////////////////////////////////////// return getStartOffset() + region.getTextEnd(); } public int getTextEndOffset(ITextRegion containedRegion) { int result = 0; if (regions != null) { int length = getRegions().size(); for (int i = 0; i < length; i++) { ITextRegion region = getRegions().get(i); if (region == containedRegion) { result = getStartOffset(region) + region.getTextEnd(); break; } } } return result; } public int getTextLength() { return textLength; } public String getType() { return type; } public void setLength(int i) { length = i; } public void setParent(ITextRegionCollection parentRegion) { parent = parentRegion; } public void setRegions(ITextRegionList containedRegions) { regions = containedRegions; } public void setStart(int i) { start = i; } public void setTextLength(int i) { textLength = i; } public void setType(String string) { type = string; } public String toString() { String className = getClass().getName(); String shortClassName = className.substring(className.lastIndexOf(".") + 1); //$NON-NLS-1$ String result = "Container!!! " + shortClassName + "--> " + getType() + ": " + getStart() + "-" + getTextEnd() + (getTextEnd() != getEnd() ? ("/" + getEnd()) : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ return result; } public StructuredDocumentEvent updateRegion(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent result = null; // FUTURE_TO_DO: need to implement region level parsing in // ITextRegionContainer::updateModel // never being called? return result; } }