/*
* Copyright (c) 2002-2004 Widespace, OU 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://solareclipse.sourceforge.net/legal/cpl-v10.html
*
* Contributors:
* Igor Malinin - initial contribution
*
* $Id: MultiViewPartitioner.java,v 1.10 2006-10-21 23:13:53 pombredanne Exp $
*/
package net.sourceforge.phpeclipse.ui.text.rules;
import java.util.ArrayList;
import java.util.List;
//incastrix
//import org.eclipse.jface.text.Assert;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.IDocumentPartitioningListener;
import org.eclipse.jface.text.IDocumentPartitioningListenerExtension;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TypedRegion;
import org.eclipse.jface.text.rules.IPartitionTokenScanner;
/**
* Advanced partitioner which maintains partitions as views to connected
* document. Views have own partitioners themselves. This class is designed as a
* base for complex partitioners such as for JSP, PHP, ASP, etc. languages.
*
* @author Igor Malinin
*/
public abstract class MultiViewPartitioner extends AbstractPartitioner {
class ViewListener implements IDocumentPartitioningListener,
IDocumentPartitioningListenerExtension {
/*
* @see org.eclipse.jface.text.IDocumentPartitioningListener#documentPartitioningChanged(IDocument)
*/
public void documentPartitioningChanged(IDocument document) {
IDocumentView view = (IDocumentView) document;
int start = view.getParentOffset(0);
int end = view.getParentOffset(view.getLength());
rememberRegion(start, end - start);
}
/*
* @see org.eclipse.jface.text.IDocumentPartitioningListenerExtension#documentPartitioningChanged(IDocument,
* IRegion)
*/
public void documentPartitioningChanged(IDocument document,
IRegion region) {
IDocumentView view = (IDocumentView) document;
int offset = region.getOffset();
int start = view.getParentOffset(offset);
int end = view.getParentOffset(offset + region.getLength());
rememberRegion(start, end - start);
}
}
private ViewListener viewListener = new ViewListener();
private OuterDocumentView outerDocument;
private DocumentEvent outerDocumentEvent;
public MultiViewPartitioner(IPartitionTokenScanner scanner) {
super(scanner);
}
public void setOuterPartitioner(IDocumentPartitioner partitioner) {
if (outerDocument == null) {
if (partitioner == null) {
return;
}
outerDocument = new OuterDocumentView(document, nodes);
outerDocument.addDocumentPartitioningListener(viewListener);
}
IDocumentPartitioner old = outerDocument.getDocumentPartitioner();
if (old != null) {
outerDocument.setDocumentPartitioner(null);
old.disconnect();
}
if (partitioner != null) {
partitioner.connect(outerDocument);
}
outerDocument.setDocumentPartitioner(partitioner);
if (partitioner == null) {
outerDocument.removeDocumentPartitioningListener(viewListener);
outerDocument = null;
}
}
/**
* Create subpartitioner.
*
* @param contentType
* name of inner partition or null
for outer
* partition
*/
protected abstract IDocumentPartitioner createPartitioner(String contentType);
protected void addInnerRegion(FlatNode position) {
if (outerDocument != null) {
if (DEBUG) {
Assert.isTrue(position.offset >= 0, Integer
.toString(position.offset));
}
int outerOffset = outerDocument.getLocalOffset(position.offset);
// axelcl start
DocumentEvent event = null;
if (outerOffset >= 0) {
// axelcl end
event = new DocumentEvent(outerDocument, outerOffset,
position.length, null);
outerDocument.fireDocumentAboutToBeChanged(event);
}
super.addInnerRegion(position);
// axelcl start
if (event != null) {
// axelcl end
outerDocument.fireDocumentChanged(event);
}
} else {
super.addInnerRegion(position);
}
if (position instanceof ViewNode) {
// TODO: revisit condition
IDocumentPartitioner partitioner = createPartitioner(position.type);
if (partitioner != null) {
InnerDocumentView innerDocument = new InnerDocumentView(
document, (ViewNode) position);
((ViewNode) position).view = innerDocument;
partitioner.connect(innerDocument);
innerDocument.setDocumentPartitioner(partitioner);
innerDocument.addDocumentPartitioningListener(viewListener);
}
}
}
protected void removeInnerRegion(FlatNode position) {
try {
if (outerDocument != null) {
DocumentEvent event = null;
if (position.offset >= 0 && position.length >= 0) {
int outerOffset = outerDocument
.getLocalOffset(position.offset);
if (outerOffset > 0) {
event = new DocumentEvent(outerDocument, outerOffset,
0, document.get(position.offset,
position.length));
outerDocument.fireDocumentAboutToBeChanged(event);
}
}
super.removeInnerRegion(position);
if (position.offset >= 0) {
if (event != null) {
outerDocument.fireDocumentChanged(event);
}
}
} else {
super.removeInnerRegion(position);
}
if (position instanceof ViewNode) {
// TODO: revisit condition
InnerDocumentView innerDocument = ((ViewNode) position).view;
if (innerDocument != null) {
IDocumentPartitioner partitioner = innerDocument
.getDocumentPartitioner();
innerDocument
.removeDocumentPartitioningListener(viewListener);
innerDocument.setDocumentPartitioner(null);
partitioner.disconnect();
}
}
} catch (BadLocationException e) {
}
}
protected void deleteInnerRegion(FlatNode position) {
super.deleteInnerRegion(position);
if (position instanceof ViewNode) {
// TODO: revisit condition
InnerDocumentView innerDocument = ((ViewNode) position).view;
if (innerDocument != null) {
IDocumentPartitioner partitioner = innerDocument
.getDocumentPartitioner();
innerDocument.removeDocumentPartitioningListener(viewListener);
innerDocument.setDocumentPartitioner(null);
partitioner.disconnect();
}
}
}
public void connect(IDocument document) {
// outerDocument = new OuterDocumentView(document, innerPositions);
super.connect(document);
setOuterPartitioner(createPartitioner(null));
// IDocumentPartitioner partitioner =
// partitioner.connect(outerDocument);
// outerDocument.setDocumentPartitioner(partitioner);
// outerDocument.addDocumentPartitioningListener(viewListener);
}
public void disconnect() {
try {
if (outerDocument != null) {
outerDocument.removeDocumentPartitioningListener(viewListener);
IDocumentPartitioner partitioner = outerDocument
.getDocumentPartitioner();
outerDocument.setDocumentPartitioner(null);
partitioner.disconnect();
}
} finally {
// TODO: cleanup listeners
outerDocument = null;
}
}
/*
* @see org.eclipse.jface.text.IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
*/
public void documentAboutToBeChanged(DocumentEvent event) {
super.documentAboutToBeChanged(event);
outerDocumentEvent = null;
int offset = event.getOffset();
int length = event.getLength();
int end = offset + length;
// find left partition
int first = computeFlatNodeIndex(offset);
if (first > 0) {
FlatNode p = (FlatNode) nodes.get(first - 1);
int right = p.offset + p.length;
if (offset < right) {
// change overlaps with partition
InnerDocumentView innerDocument = null;
if (p instanceof ViewNode) {
// TODO: revisit condition
innerDocument = ((ViewNode) p).view;
}
if (end < right) {
if (innerDocument != null) {
// cahnge completely inside partition
int start = innerDocument.getLocalOffset(offset);
innerDocument
.fireDocumentAboutToBeChanged(new DocumentEvent(
innerDocument, start, length, event
.getText()));
}
return;
}
if (innerDocument != null) {
// cut partition at right
int start = innerDocument.getLocalOffset(offset);
innerDocument
.fireDocumentAboutToBeChanged(new DocumentEvent(
innerDocument, start, innerDocument
.getLength()
- start, null));
}
}
}
// find right partition
int last = computeFlatNodeIndex(end);
if (last > 0) {
FlatNode p = (FlatNode) nodes.get(last - 1);
if (p instanceof ViewNode) {
// TODO: revisit condition
InnerDocumentView innerDocument = ((ViewNode) p).view;
if (innerDocument != null) {
int right = p.offset + p.length;
if (end < right) {
// cut partition at left
int cut = innerDocument.getLocalOffset(end);
innerDocument
.fireDocumentAboutToBeChanged(new DocumentEvent(
innerDocument, 0, cut, null));
}
}
}
}
if (outerDocument != null) {
int left = outerDocument.getLocalOffset(offset);
int right = outerDocument.getLocalOffset(end);
String text = event.getText();
if (left >= 0 && (right - left >= 0)) {
if (left != right || text != null && text.length() > 0) {
outerDocumentEvent = new DocumentEvent(outerDocument, left,
right - left, text);
outerDocument
.fireDocumentAboutToBeChanged(outerDocumentEvent);
}
}
}
}
protected int fixupPartitions(DocumentEvent event) {
int offset = event.getOffset();
int length = event.getLength();
int end = offset + length;
// fixup/notify inner views laying on change boundaries
int first = computeFlatNodeIndex(offset);
if (first > 0) {
FlatNode p = (FlatNode) nodes.get(first - 1);
int right = p.offset + p.length;
if (offset < right) {
// change overlaps with partition
if (end < right) {
// cahnge completely inside partition
String text = event.getText();
p.length -= length;
if (text != null) {
p.length += text.length();
}
if (p instanceof ViewNode) {
// TODO: revisit condition
InnerDocumentView innerDocument = ((ViewNode) p).view;
if (innerDocument != null) {
int start = innerDocument.getLocalOffset(offset);
innerDocument
.fireDocumentChanged(new DocumentEvent(
innerDocument, start, length, text));
}
}
} else {
// cut partition at right
int cut = p.offset + p.length - offset;
p.length -= cut;
if (p instanceof ViewNode) {
// TODO: revisit condition
InnerDocumentView innerDocument = ((ViewNode) p).view;
if (innerDocument != null) {
int start = innerDocument.getLocalOffset(offset);
// TODO: ???fireDocumentAboutToBeChanged???
innerDocument
.fireDocumentChanged(new DocumentEvent(
innerDocument, start, cut, null));
}
}
}
}
}
int last = computeFlatNodeIndex(end);
if (last > 0 && first != last) {
FlatNode p = (FlatNode) nodes.get(last - 1);
int right = p.offset + p.length;
if (end < right) {
// cut partition at left
int cut = end - p.offset;
p.length -= cut;
p.offset = offset;
String text = event.getText();
if (text != null) {
p.offset += text.length();
}
if (p instanceof ViewNode) {
// TODO: revisit condition
InnerDocumentView innerDocument = ((ViewNode) p).view;
if (innerDocument != null) {
// TODO: ???fireDocumentAboutToBeChanged???
innerDocument.fireDocumentChanged(new DocumentEvent(
innerDocument, 0, cut, null));
}
}
--last;
}
}
// fixup inner views laying afrer change
String text = event.getText();
if (text != null) {
length -= text.length();
}
for (int i = last, size = nodes.size(); i < size; i++) {
((FlatNode) nodes.get(i)).offset -= length;
}
// delete inner views laying completely inside change boundaries
if (first < last) {
do {
deleteInnerRegion((FlatNode) nodes.get(--last));
} while (first < last);
rememberRegion(offset, 0);
}
// notify outer view
if (outerDocumentEvent != null) {
outerDocument.fireDocumentChanged(outerDocumentEvent);
}
return first;
}
/*
* @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int,
* int)
*/
protected String getContentType(String parent, String view) {
if (view != null) {
return view;
}
if (parent != null) {
return parent;
}
return IDocument.DEFAULT_CONTENT_TYPE;
}
/*
* @see org.eclipse.jface.text.IDocumentPartitioner#computePartitioning(int,
* int)
*/
public ITypedRegion[] computePartitioning(int offset, int length) {
List list = new ArrayList();
// if (DEBUG) {
// if (length>=9400) {
// length--;
// }
// System.out.print("MultiViewPartitioner::computePartitioning - Offset:
// ");
// System.out.print(offset);
// System.out.print(", Length: ");
// System.out.print(length);
// System.out.println("");
// }
int end = offset + length;
int index = computeFlatNodeIndex(offset);
while (true) {
FlatNode prev = (index > 0) ? (FlatNode) nodes.get(index - 1)
: null;
if (prev != null) {
if (prev.overlapsWith(offset, length)) {
addInnerPartitions(list, offset, length, prev);
}
if (end <= prev.offset + prev.length) {
break;
}
}
FlatNode next = (index < nodes.size()) ? (FlatNode) nodes
.get(index) : null;
if (next == null || offset < next.offset) {
addOuterPartitions(list, offset, length, prev, next);
}
if (next == null) {
break;
}
++index;
}
// if (DEBUG) {
// showList(list);
// }
return (TypedRegion[]) list.toArray(new TypedRegion[list.size()]);
}
// private void showList(List list) {
// try {
// throw new NullPointerException();
// } catch (Exception e) {
// e.printStackTrace();
// }
// System.out.println(">>>>>List start");
// TypedRegion temp;
// for (int i = 0; i < list.size(); i++) {
// temp = (TypedRegion) list.get(i);
// System.out.print("Offset: ");
// System.out.print(temp.getOffset());
// System.out.print(", Length: ");
// System.out.print(temp.getLength());
// System.out.print(", Type: ");
// System.out.print(temp.getType());
// System.out.println("");
// }
// System.out.println("<<<<= 0) {
ITypedRegion[] regions = null;
try {
regions = outerDocument.computePartitioning(start, len);
} catch (Exception e) {
// nasty workaround, which prevents cursor from moveing
// backwards in the editor
// but doesn't solve the partitioning problem
regions = new ITypedRegion[0];
System.out
.println("MultiViewerPartitioner#addOuterPartitions failure\n"
+ "start:"
+ start
+ " length:"
+ len
+ "\n" + outerDocument.get(start, len));
}
for (int i = 0; i < regions.length; i++) {
ITypedRegion region = regions[i];
// convert back to parent offsets
start = outerDocument.getParentOffset(region.getOffset());
end = start + region.getLength();
if (prev != null) {
offset = prev.offset + prev.length;
if (start < offset) {
start = offset;
}
}
if (next != null) {
offset = next.offset;
if (offset < end) {
end = offset;
}
}
// if (DEBUG) {
// if (end - start<0) {
// showList(list);
// System.out.print("MultiViewPartitioner::addOuterPartitions
// - Offset: ");
// System.out.print(offset);
// System.out.print(", Start: ");
// System.out.print(start);
// System.out.print(", End: ");
// System.out.print(end);
// System.out.print(", Type: ");
// System.out.print(region.getType());
// System.out.println("");
// throw new IndexOutOfBoundsException();
// }
// }
list.add(new TypedRegion(start, end - start,
getContentType(null, region.getType())));
}
}
} catch (BadLocationException x) {
}
}
private void addInnerPartitions(List list, int offset, int length,
FlatNode position) {
InnerDocumentView innerDocument = null;
if (position instanceof ViewNode) {
// TODO: revisit condition
innerDocument = ((ViewNode) position).view;
}
if (innerDocument == null) {
// simple partition
// if (DEBUG) {
// if (position.length<0) {
// throw new IndexOutOfBoundsException();
// }
// }
list.add(new TypedRegion(position.offset, position.length,
getContentType(position.type, null)));
return;
}
// multiplexing to inner view
try {
// limit region
int start = Math.max(offset, position.offset);
int end = Math.min(offset + length, position.offset
+ position.length);
// convert to document offsets
length = end - start;
offset = innerDocument.getLocalOffset(start);
ITypedRegion[] regions = innerDocument.computePartitioning(offset,
length);
for (int i = 0; i < regions.length; i++) {
ITypedRegion region = regions[i];
// convert back to parent offsets
offset = innerDocument.getParentOffset(region.getOffset());
length = region.getLength();
// if (DEBUG) {
// if (length<0) {
// throw new IndexOutOfBoundsException();
// }
// }
list.add(new TypedRegion(offset, length, getContentType(
position.type, region.getType())));
}
} catch (BadLocationException x) {
}
}
/*
* @see org.eclipse.jface.text.IDocumentPartitioner#getPartition(int)
*/
public ITypedRegion getPartition(int offset) {
if (nodes.size() == 0) {
return getOuterPartition(offset, null, null);
}
int index = computeFlatNodeIndex(offset);
if (index < nodes.size()) {
FlatNode next = (FlatNode) nodes.get(index);
if (offset == next.offset) {
return getInnerPartition(offset, next);
}
if (index == 0) {
return getOuterPartition(offset, null, next);
}
FlatNode prev = (FlatNode) nodes.get(index - 1);
if (prev.includes(offset)) {
return getInnerPartition(offset, prev);
}
return getOuterPartition(offset, prev, next);
}
FlatNode prev = (FlatNode) nodes.get(nodes.size() - 1);
if (prev.includes(offset)) {
return getInnerPartition(offset, prev);
}
return getOuterPartition(offset, prev, null);
}
protected ITypedRegion getOuterPartition(int offset, FlatNode prev,
FlatNode next) {
try {
int start, end;
String type;
if (outerDocument == null) {
start = 0;
end = document.getLength();
type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
} else {
int outerOffset = outerDocument.getLocalOffset(offset);
// axelcl start
if (outerOffset < 0) {
start = 0;
end = document.getLength();
type = getContentType(null, IDocument.DEFAULT_CONTENT_TYPE);
} else {
// axelcl end
ITypedRegion region = outerDocument
.getPartition(outerOffset);
start = region.getOffset();
end = start + region.getLength();
// convert to parent offset
start = outerDocument.getParentOffset(start);
end = outerDocument.getParentOffset(end);
type = getContentType(null, region.getType());
}
}
if (prev != null) {
offset = prev.offset + prev.length;
if (start < offset) {
start = offset;
}
}
if (next != null) {
offset = next.offset;
if (offset < end) {
end = offset;
}
}
return new TypedRegion(start, end - start, type);
} catch (BadLocationException x) {
x.printStackTrace();
throw new IllegalArgumentException();
}
}
protected ITypedRegion getInnerPartition(int offset, FlatNode position) {
if (position instanceof ViewNode) {
// TODO: revisit condition
InnerDocumentView innerDocument = ((ViewNode) position).view;
if (innerDocument != null) {
// multiplexing to inner view
try {
// convert to inner offset
ITypedRegion region = innerDocument
.getPartition(innerDocument.getLocalOffset(offset));
// convert to parent offset
offset = innerDocument.getParentOffset(region.getOffset());
return new TypedRegion(offset, region.getLength(),
getContentType(position.type, region.getType()));
} catch (BadLocationException x) {
}
}
}
// simple partition
return new TypedRegion(position.offset, position.length, position.type);
}
}