blob: d8ae620c5c8e8bc882212d833953c7a25a337ea7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.text;
import java.util.Arrays;
import java.util.Comparator;
/**
* Implementation of <code>IDocumentInformationMapping</code> matching <code>ProjectionDocument</code> and
* <code>ProjectionDocumentManager</code>. The parent document is considered the original document, the projection
* document is considered the image document.<p>
* This class is for internal use only.
* @since 2.1
*/
public class CoordinatesTranslator implements IDocumentInformationMapping {
/** The parent document */
private IDocument fParentDocument;
/** The position category used to manage the projected regions of the parent document */
private String fParentCategory;
/** The projection document */
private ProjectionDocument fProjectionDocument;
/** The position category to manage the fragments of the projection document. */
private String fProjectionCategory;
/**
* Creates a new mapping between the given parent document and the given projection document.
*
* @param parent the parent document
* @param parentCategory the position category of the parent document used to manage the projected regions
* @param projection the projection document
* @param projectionCategory the position category of the projection document used to manage the fragments
*/
public CoordinatesTranslator(IDocument parent, String parentCategory, ProjectionDocument projection, String projectionCategory) {
fParentDocument= parent;
fParentCategory= parentCategory;
fProjectionDocument= projection;
fProjectionCategory= projectionCategory;
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginOffset(int)
*/
public int toOriginOffset(int imageOffset) throws BadLocationException {
Fragment fragment= (Fragment) getPositionOfOffset(fProjectionDocument, ProjectionDocument.FRAGMENT_CATEGORY, imageOffset);
if (fragment == null) {
if (imageOffset == 0)
return 0;
throw new BadLocationException();
}
int relative= imageOffset - fragment.offset;
return fragment.getOrigin().offset + relative;
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginRegion(org.eclipse.jface.text.IRegion)
*/
public IRegion toOriginRegion(IRegion imageRegion) throws BadLocationException {
int projectionOffset= imageRegion.getOffset();
int projectionLength= imageRegion.getLength();
if (projectionLength == 0) {
if (projectionOffset == 0 && projectionLength == fProjectionDocument.getLength())
return new Region(0, fParentDocument.getLength());
return new Region(toOriginOffset(projectionOffset), 0);
}
int o1= toOriginOffset(projectionOffset);
int o2= toOriginOffset(projectionOffset + projectionLength -1);
return new Region(o1, o2 - o1 + 1);
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLines(int)
*/
public IRegion toOriginLines(int imageLine) throws BadLocationException {
IRegion projectionDocumentRegion= fProjectionDocument.getLineInformation(imageLine);
IRegion parentDocumentRegion= toOriginRegion(projectionDocumentRegion);
int startLine= fParentDocument.getLineOfOffset(parentDocumentRegion.getOffset());
if (parentDocumentRegion.getLength() == 0)
return new Region(startLine, 0);
int endLine= fParentDocument.getLineOfOffset(parentDocumentRegion.getOffset() + parentDocumentRegion.getLength() -1);
return new Region(startLine, endLine - startLine);
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLine(int)
*/
public int toOriginLine(int imageLine) throws BadLocationException {
IRegion lines= toOriginLines(imageLine);
if (lines.getLength() > 0)
throw new IllegalStateException();
return lines.getOffset();
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#toImageOffset(int)
*/
public int toImageOffset(int originOffset) throws BadLocationException {
ProjectionPosition projection= (ProjectionPosition) getPositionOfOffset(fParentDocument, ProjectionDocumentManager.PROJECTION_DOCUMENTS, originOffset);
if (projection != null)
return translateOffset(projection, originOffset, projection.getFragment());
// not included
return -1;
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#toImageRegion(org.eclipse.jface.text.IRegion)
*/
public IRegion toImageRegion(IRegion originRegion) throws BadLocationException {
if (originRegion.getLength() == 0) {
int projectionOffset= toImageOffset(originRegion.getOffset());
return projectionOffset == -1 ? null : new Region(projectionOffset, 0);
}
Position[] positions= getPositionsOfRange(fParentDocument, ProjectionDocumentManager.PROJECTION_DOCUMENTS, originRegion, null);
if (positions != null && positions.length > 0) {
ProjectionPosition projection= (ProjectionPosition) positions[0];
int offset= originRegion.getOffset();
int length= originRegion.getLength();
int delta= projection.getOffset() - offset;
if (delta > 0) {
offset += delta;
length -= delta;
}
int start= translateOffset(projection, offset, projection.getFragment());
projection= (ProjectionPosition) positions[positions.length -1];
int decrease= 0;
int endOffset= offset + length;
if (length > 0)
decrease= 1;
endOffset -= decrease;
delta= endOffset - (projection.getOffset() + Math.max(projection.getLength() -1, 0));
if (delta > 0)
endOffset -= delta;
int end= translateOffset(projection, endOffset, projection.getFragment());
return new Region(start, end - start + decrease);
}
return null;
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#toImageLine(int)
*/
public int toImageLine(int originLine) throws BadLocationException {
IRegion parentDocumentRegion= fParentDocument.getLineInformation(originLine);
IRegion projectionDocumentRegion= toImageRegion(parentDocumentRegion);
if (projectionDocumentRegion == null)
return -1;
int startLine= fProjectionDocument.getLineOfOffset(projectionDocumentRegion.getOffset());
if (projectionDocumentRegion.getLength() == 0)
return startLine;
int endLine= fProjectionDocument.getLineOfOffset(projectionDocumentRegion.getOffset() + projectionDocumentRegion.getLength() -1);
if (endLine != startLine)
throw new IllegalStateException();
return startLine;
}
//-----------------------------------------------------------------------------------------------------------------------------------
/**
* Translates the given offset relative to the given origin position into an
* offset relative to the given target position.
*
* @param origin the origin position
* @param originOffset the offset relative to <code>originOffset</code>
* @param target the target position
* @return <code>originOffset</code> translated to the given target position
*/
private int translateOffset(Position origin, int originOffset, Position target) {
int relative= originOffset - origin.offset;
return target.offset + relative;
}
/**
* Returns the position of the given category of the given document that includes the given offset
* or <code>null</code> if there is no such position.
*
* @param document the document
* @param category the position category of <code>document</code>
* @param offset the offset into <code>document</code>
* @return the position including the given offset or <code>null</code>
* @throws BadLocationException if <code>offset</code> is not valid in the given document
*/
private Position getPositionOfOffset(IDocument document, String category, int offset) throws BadLocationException {
try {
int index= getPositionIndexOfOffset(document, category, offset, 0);
if (index > -1) {
Position[] positions= document.getPositions(category);
return positions[index];
}
} catch (BadPositionCategoryException x) {
}
return null;
}
/**
* Returns an array of positions of the given category that cover the given range of the document.
*
* @param document the document
* @param category the position category of <code>document</code>
* @param range the range of <code>document</code>
* @param comparator the comparator to sort the array to be returned
* @return an array of positions that cover the given range in the given document
*/
private Position[] getPositionsOfRange(IDocument document, String category, IRegion range, Comparator comparator) {
int offset= range.getOffset();
int length= range.getLength();
try {
int start= getPositionIndexOfOffset(document, category, offset, length);
int end= getPositionIndexOfOffset(document, category, offset + length -1, 1 - length);
if (start > -1 && end > -1) {
Position[] positions= document.getPositions(category);
if (start == end)
return new Position[] { positions[start] };
Position[] result= new Position[end - start + 1];
for (int i= start; i <= end; i++)
result[i - start]= positions[i];
if (comparator != null)
Arrays.sort(result, comparator);
return result;
}
} catch (BadPositionCategoryException e) {
} catch (BadLocationException e) {
}
return new Position[0];
}
/**
* Returns the index of the position of the given category of the given document that includes the
* given offset. <code>direction</code> indicates the direction into which the algorithm should search.
*
* @param document the document
* @param category the position category of <code>document</code>
* @param offset the offset into <code>document</code>
* @param direction the search direction
* @return the index of the position
* @throws BadPositionCategoryException if <code>category</code> is not valid in <code>document</code>
* @throws BadLocationException if <code>offset</code> is not valid in <code>document</code>
*/
private int getPositionIndexOfOffset(IDocument document, String category, int offset, int direction ) throws BadPositionCategoryException, BadLocationException{
Position[] positions= document.getPositions(category);
if (positions != null && positions.length > 0) {
// test for inclusion
int index= document.computeIndexInCategory(category, offset);
if (index < positions.length && positions[index].includes(offset))
return index;
if (index > 0 && positions[index -1].includes(offset))
return index -1;
// find next accorrding to direction
if (direction != 0) {
if (direction > 0) {
if (index < positions.length && positions[index].overlapsWith(offset, direction))
return index;
} else {
if (index > 0 && positions[index -1].overlapsWith(offset + direction, -direction))
return index -1;
}
}
}
return -1;
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#getCoverage()
*/
public IRegion getCoverage() {
Position coverage= fProjectionDocument.getParentDocumentCoverage();
return new Region(coverage.getOffset(), coverage.getLength());
}
/*
* @see org.eclipse.jface.text.IDocumentInformationMapping#toClosestImageLine(int)
*/
public int toClosestImageLine(int originLine) throws BadLocationException {
try {
int modelLineOffset= fParentDocument.getLineOffset(originLine);
int index= fParentDocument.computeIndexInCategory(ProjectionDocumentManager.PROJECTION_DOCUMENTS, modelLineOffset);
Position[] projections= fParentDocument.getPositions(ProjectionDocumentManager.PROJECTION_DOCUMENTS);
if (index < projections.length) {
Position p= projections[index -1];
int delta1= modelLineOffset - (p.getOffset() + p.getLength());
p= projections[index];
int delta2= modelLineOffset - (p.getOffset() + p.getLength());
if (delta1 < delta2) {
p= projections[index -1];
originLine= fParentDocument.getLineOfOffset(p.getOffset() + Math.max(p.getLength() -1, 0));
} else {
originLine= fParentDocument.getLineOfOffset(p.getOffset());
}
} else if (projections.length > 0) {
Position p= projections[index -1];
originLine= fParentDocument.getLineOfOffset(p.getOffset() + Math.max(p.getLength() -1, 0));
} else {
return 0;
}
return toImageLine(originLine);
} catch (BadLocationException x) {
} catch (BadPositionCategoryException x) {
}
return 0;
}
}