package de.ovgu.cide.editor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.presentation.IPresentationRepairer;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import cide.gast.ASTVisitor;
import cide.gast.IASTNode;
import cide.gparser.ParseException;
import cide.gparser.TokenMgrError;
import de.ovgu.cide.features.IFeature;
import de.ovgu.cide.features.source.ColoredSourceFile;
import de.ovgu.cide.features.source.SourceFileColorManager;
import de.ovgu.cide.utils.ColorHelper;
public class ColorRepairer implements IPresentationRepairer {
private final ColoredSourceFile sourceFile;
private ColoredTextEditor editor;
// currently just deactivated
private boolean hideInvisibleCode = false;
public ColorRepairer(ColoredSourceFile sourceFile, ColoredTextEditor editor) {
this.sourceFile = sourceFile;
this.editor = editor;
}
public void createPresentation(TextPresentation presentation,
ITypedRegion damage) {
if (sourceFile==null || !sourceFile.isColored()) {
editor.markNotColored();
return;
}
if (editor.isDirty()) {
editor.markFileEdited();
return;
}
try {
long start = System.currentTimeMillis();
IASTNode root = sourceFile.getAST();
if (root != null) {
List<CodeSegment> segments = CodeSegmentCalculator
.getCodeSegments(root, sourceFile.getColorManager());
if (hideInvisibleCode)
segments = grayInvisibleCode(segments, root, sourceFile
.getColorManager(), sourceFile.getFeatureModel()
.getVisibleFeatures());
for (CodeSegment segment : segments)
processSegment(presentation, damage, segment);
}
editor.markASTOk(System.currentTimeMillis() - start);
} catch (CoreException e) {
// TODO Auto-generated catch block
editor.markCoreException(e);
} catch (TokenMgrError e) {
editor.markParseException(e);
} catch (ParseException e) {
editor.markParseException(e);
}
}
/**
* experimental implementation to change the foreground color of features
* marked invisible to gray
*
*
* @param segments
* previous segmentation for background colors
* @param ast
* @param colorManager
* @param visibleFeatures
* visible features, all others are grayed out
* @return updated segments
*/
private List<CodeSegment> grayInvisibleCode(List<CodeSegment> segments,
IASTNode ast, SourceFileColorManager colorManager,
Collection<IFeature> visibleFeatures) {
ArrayList<CodeSegment> result = new ArrayList<CodeSegment>();
List<Position> hiddenSegments = getHiddenSegments(ast, colorManager,
visibleFeatures);
List<Integer> splitPositions = getSplitPositions(hiddenSegments);
// split code segments according to hidden parts
for (CodeSegment seg : segments) {
for (int s : splitPositions) {
if (s > seg.offset && s < seg.endPosition()) {
CodeSegment firstPart = seg.copy();
firstPart.setEndPosition(s);
seg.moveStartPosition(s);
result.add(firstPart);
}
}
result.add(seg);
}
// set (previously split) code segments as hidden where appropiate
for (CodeSegment seg : result) {
for (Position hiddenSeg : hiddenSegments) {
if (covers(hiddenSeg, seg))
seg.isHidden = true;
}
}
return result;
}
private List<Integer> getSplitPositions(List<Position> segs) {
List<Integer> result = new ArrayList<Integer>();
for (Position seg : segs) {
if (!result.contains(seg.offset))
result.add(seg.offset);
if (!(result.contains(seg.offset + seg.length)))
result.add(seg.offset + seg.length);
}
return result;
}
private List<Position> getHiddenSegments(IASTNode ast,
final SourceFileColorManager colorManager,
final Collection<IFeature> visibleFeatures) {
final LinkedList<Position> invisibleSegments = new LinkedList<Position>();
ast.accept(new ASTVisitor() {
private Stack<List<IASTNode>> visibleNodes = new Stack<List<IASTNode>>();
@Override
public boolean visit(IASTNode node) {
visibleNodes.push(new ArrayList<IASTNode>());
return super.visit(node);
}
@Override
public void postVisit(IASTNode node) {
List<IASTNode> visibleChildren = visibleNodes.pop();
boolean isVisible = !visibleChildren.isEmpty();
if (!isVisible
&& overlap(colorManager.getColors(node),
visibleFeatures))
isVisible = true;
if (isVisible && !visibleNodes.isEmpty())
visibleNodes.peek().add(node);
if (node.isOptional() && !isVisible) {
Position newPos = new Position(node.getStartPosition(),
node.getLength());
// avoid overlapping segments
while (!invisibleSegments.isEmpty()
&& covers(newPos, invisibleSegments.getLast()))
invisibleSegments.removeLast();
invisibleSegments.add(newPos);
}
super.postVisit(node);
}
private boolean overlap(Collection<IFeature> s1,
Collection<IFeature> s2) {
for (IFeature f : s1)
if (s2.contains(f))
return true;
return false;
}
});
return invisibleSegments;
}
private boolean covers(Position outer, Position inner) {
if (outer.offset <= inner.offset)
if (outer.offset + outer.length >= inner.offset + inner.length)
return true;
return false;
}
private void processSegment(TextPresentation presentation,
ITypedRegion damage, CodeSegment segment) {
// colors
if (inRange(segment, damage))
presentation.addStyleRange(createStyleRange(segment));
// // projection
// InlineProjectionAnnotationModel annotationModel =
// editor.getSourceViewerI()
// .getInlineProjectionAnnotationModel();
// if (annotationModel != null) {
// ColoredInlineProjectionAnnotation annotation = new
// ColoredInlineProjectionAnnotation();
// annotation.setColors(segment.getColors());
// Position pos = new Position(segment.offset, segment.length);
// annotation.setPosition(pos);
// annotation.adjustCollapsing(editor.getProjectionColorManager()
// .getExpandedColors());
// HashMap newAnnotations = new HashMap();
// newAnnotations.put(annotation, pos);
//
// annotationModel.modifyAnnotations(null, newAnnotations, null);
// }
}
public void setDocument(IDocument document) {
// this.document = document;
}
private Color gray = new Color(Display.getDefault(), 128, 128, 128);
/**
* @return Returns a corresponding style range.
*/
public StyleRange createStyleRange(CodeSegment segment) {
Color background = null;
if (!segment.getColors().isEmpty())
background = ColorHelper.getCombinedColor(segment.getColors());
Color foreground = null;
if (segment.isHidden)
foreground = gray;
StyleRange styleRange = new StyleRange(segment.getOffset(), segment
.getLength(), foreground, background/* , fontStyle */);
return styleRange;
}
private boolean inRange(CodeSegment node, ITypedRegion damage) {
if (node.getOffset() + node.getLength() < damage.getOffset())
return false;
if (node.getOffset() > damage.getOffset() + damage.getLength())
return false;
return true;
}
// protected void updateInlineProjectionAnnotations(
// List<CodeSegment> addedPositions,
// ColoredCompilationUnitEditor editor) {
//
// ColoredInlineProjectionAnnotation[] annotations = new
// ColoredInlineProjectionAnnotation[addedPositions
// .size()];
//
// // this will hold the new annotations along
// // with their corresponding positions
// HashMap newAnnotations = new HashMap();
// addedPositions = new ArrayList<CodeSegment>(addedPositions);
// ArrayList<ColoredInlineProjectionAnnotation> knownPositions = new
// ArrayList<ColoredInlineProjectionAnnotation>(
// oldAnnotations);
// ArrayList<ColoredInlineProjectionAnnotation> savedPositions = new
// ArrayList<ColoredInlineProjectionAnnotation>();
//
// // move unchanged segments from known to saved (those are not deleted)
// for (Iterator<CodeSegment> i = addedPositions.iterator(); i.hasNext();) {
// CodeSegment seg = i.next();
// for (Iterator<ColoredInlineProjectionAnnotation> a = knownPositions
// .iterator(); a.hasNext();) {
// ColoredInlineProjectionAnnotation known = a.next();
// if (seg.offset == known.getPosition().getOffset()
// && seg.length == known.getPosition().getLength()
// && seg.getColors().equals(known.getColors())) {
// i.remove();
// a.remove();
// savedPositions.add(known);
// }
// }
//
// }
//
// for (int i = 0; i < addedPositions.size(); i++) {
// ColoredInlineProjectionAnnotation annotation = new
// ColoredInlineProjectionAnnotation();
// annotation.setColors(addedPositions.get(i).getColors());
// Position pos = new Position(addedPositions.get(i).offset,
// addedPositions.get(i).length);
// annotation.setPosition(pos);
// annotation.adjustCollapsing(editor.getProjectionColorManager()
// .getExpandedColors());
// newAnnotations.put(annotation, pos);
//
// annotations[i] = annotation;
// }
//
// if (annotationModel != null) {
// Annotation[] deletedAnnotations = new Annotation[knownPositions
// .size()];
// deletedAnnotations = knownPositions.toArray(deletedAnnotations);
// annotationModel.modifyAnnotations(deletedAnnotations,
// newAnnotations, null);
// }
// oldAnnotations = savedPositions;
// oldAnnotations.addAll(newAnnotations.keySet());
// }
}