/*******************************************************************************
* Copyright 2017 Capital One Services, LLC and Bitwise, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package hydrograph.ui.expression.editor.sourceviewer;
import hydrograph.ui.expression.editor.PathConstant;
import hydrograph.ui.expression.editor.jar.util.BuildExpressionEditorDataSturcture;
import hydrograph.ui.expression.editor.javasourceviewerconfiguration.HotKeyUtil;
import hydrograph.ui.expression.editor.javasourceviewerconfiguration.HydrographJavaSourceViewerConfiguration;
import hydrograph.ui.expression.editor.styletext.ExpressionEditorStyledText;
import hydrograph.ui.logging.factory.LogFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.text.java.hover.SourceViewerInformationControl;
import org.eclipse.jdt.ui.text.IColorManager;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.ui.text.JavaTextTools;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.FindReplaceDocumentAdapter;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IPositionUpdater;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.OverviewRuler;
import org.eclipse.jface.text.source.projection.ProjectionAnnotation;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.internal.editors.text.EditorsPlugin;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.DefaultRangeIndicator;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.slf4j.Logger;
/**
* Responsible to create a sourceviewer for expression editor.
*
*/
public class SourceViewer extends ProjectionViewer {
private Logger LOGGER = LogFactory.INSTANCE.getLogger(SourceViewer.class);
private static final String HYDROGRAPH_COMPILATIONUNIT_PACKAGE = "hydrograph.compilationunit";
private IRegion viewerStartRegion, viewerEndRegion;
private static int currentId = 0;
private String filename;
private SourceViewerDecorationSupport fSourceViewerDecorationSupport;
private static String className;
private ISharedTextColors sharedColors;
private ICompilationUnit compilatioUnit;
private IOverviewRuler fOverviewRuler;
private MarkerAnnotationPreferences fAnnotationPreferences;
private IFile file = null;
private IAnnotationAccess annotationAccess;
private int oldDocLength;
private static final String CURRENT_LINE = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE;
private static final String CURRENT_LINE_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_CURRENT_LINE_COLOR;
private static final String PRINT_MARGIN = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN;
private static final String PRINT_MARGIN_COLOR = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLOR;
private static final String PRINT_MARGIN_COLUMN = AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLUMN;
private Map<ProjectionAnnotation, Position> oldAnnotations;
public static final String VIEWER_CLASS_NAME = "CustomCompilationUnit";
public SourceViewer(Composite parent, IVerticalRuler verticalRuler, IOverviewRuler overviewRuler,
boolean showAnnotationsOverview, int styles, IAnnotationAccess annotationAccess, ISharedTextColors sharedColors,
IDocument document)
{
super(parent, verticalRuler, overviewRuler, showAnnotationsOverview, SWT.BOLD);
int id = currentId++;
filename = VIEWER_CLASS_NAME + id++ + ".java";
this.sharedColors=sharedColors;
this.annotationAccess=annotationAccess;
this.fOverviewRuler=overviewRuler;
oldAnnotations= new HashMap<ProjectionAnnotation, Position>();
IJavaProject javaProject = JavaCore.create(BuildExpressionEditorDataSturcture.INSTANCE.getCurrentProject());
try
{
IPackageFragmentRoot[] ipackageFragmentRootList=javaProject.getPackageFragmentRoots();
IPackageFragmentRoot ipackageFragmentRoot=null;
for(IPackageFragmentRoot tempIpackageFragmentRoot:ipackageFragmentRootList)
{
if(tempIpackageFragmentRoot.getKind()==IPackageFragmentRoot.K_SOURCE
&& StringUtils.equals(PathConstant.TEMP_BUILD_PATH_SETTINGS_FOLDER,tempIpackageFragmentRoot.getPath().removeFirstSegments(1).toString()))
{
ipackageFragmentRoot=tempIpackageFragmentRoot;
break;
}
}
IPackageFragment compilationUnitPackage= ipackageFragmentRoot.createPackageFragment(HYDROGRAPH_COMPILATIONUNIT_PACKAGE, true, new NullProgressMonitor());
compilatioUnit= compilationUnitPackage.createCompilationUnit(filename,document.get(),true, new NullProgressMonitor());
}
catch (Exception exception) {
LOGGER.warn("Exception occurred while initializing source viewer", exception);
} finally {
if (javaProject != null) {
try {
javaProject.close();
} catch (JavaModelException javaModelException) {
LOGGER.warn("Exception occurred while closing java-project", javaModelException);
}
}
}
initializeViewer(document);
updateContents();
}
private void handleVerifyKeyPressed(VerifyEvent event) {
if (!event.doit) {
return;
}
try {
KeyStroke triggerKeyStroke = HotKeyUtil.getHotKey(HotKeyUtil.contentAssist);
if (triggerKeyStroke != null) {
if ((triggerKeyStroke.getModifierKeys() == KeyStroke.NO_KEY && triggerKeyStroke.getNaturalKey() == event.character)
||
(((triggerKeyStroke.getNaturalKey() == event.keyCode)
|| (Character.toLowerCase(triggerKeyStroke.getNaturalKey()) == event.keyCode) || (Character
.toUpperCase(triggerKeyStroke.getNaturalKey()) == event.keyCode)) && ((triggerKeyStroke
.getModifierKeys() & event.stateMask) == triggerKeyStroke.getModifierKeys()))) {
doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);
event.doit = false;
return;
}
}
}
catch (Exception e) {
}
if (event.stateMask != SWT.CTRL) {
return;
}
switch (event.character) {
case ' ':
doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);
event.doit = false;
break;
case '.':
doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS);
event.doit = false;
break;
case 'y' - 'a' + 1:
doOperation(ITextOperationTarget.REDO);
event.doit = false;
break;
case 'z' - 'a' + 1:
doOperation(ITextOperationTarget.UNDO);
event.doit = false;
break;
default:
}
}
private void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
Iterator e = fAnnotationPreferences.getAnnotationPreferences().iterator();
while (e.hasNext()) {
support.setAnnotationPreference((AnnotationPreference) e.next());
}
support.setCursorLinePainterPreferenceKeys(CURRENT_LINE, CURRENT_LINE_COLOR);
support.setMarginPainterPreferenceKeys(PRINT_MARGIN, PRINT_MARGIN_COLOR, PRINT_MARGIN_COLUMN);
support.setSymbolicFontName(JFaceResources.TEXT_FONT);
}
private void initializeViewer(IDocument document) {
fAnnotationPreferences = EditorsPlugin.getDefault().getMarkerAnnotationPreferences();
setDocument(document);
installViewerConfiguration();
setEditable(true);
Font font = JFaceResources.getFontRegistry().get(JFaceResources.TEXT_FONT);
getTextWidget().setFont(font);
getTextWidget().setData("document",document);
Control control = getControl();
GridData data = new GridData(GridData.FILL_BOTH);
control.setLayoutData(data);
prependVerifyKeyListener(new VerifyKeyListener() {
@Override
public void verifyKey(VerifyEvent event) {
handleVerifyKeyPressed(event);
}
});
addDocumentListener(document);
}
private void addDocumentListener(final IDocument document) {
final ExecutionLimiter documentReconcilerLimiter = new ExecutionLimiter(500, true) {
@Override
protected void execute(boolean isFinalExecution, Object data) {
if (isFinalExecution) {
if (getControl() != null && !getControl().isDisposed()) {
getControl().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
updateContents();
if (document.get().length() != 0) {
calculatePositions();
}
updateVisibleRegion();
}
});
}
}
}
};
document.addDocumentListener(new IDocumentListener() {
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
}
@Override
public void documentChanged(DocumentEvent event) {
documentReconcilerLimiter.resetTimer();
documentReconcilerLimiter.startIfExecutable(true, null);
}
});
document.addPositionUpdater(new IPositionUpdater() {
@Override
public void update(DocumentEvent event) {
}
});
}
private void calculatePositions() {
if (hasSnippetsModifications()) {
final Map<ProjectionAnnotation, Position> annotations = getAllSnippetsAnnotations();
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (!annotations.isEmpty() && getProjectionAnnotationModel() == null) {
enableProjection();
}
if (getProjectionAnnotationModel() != null) {
Annotation[] oldAnno = oldAnnotations.keySet().toArray(new Annotation[0]);
getProjectionAnnotationModel().modifyAnnotations(oldAnno, annotations, null);
oldAnnotations.clear();
oldAnnotations.putAll(annotations);
if (annotations.isEmpty()) {
disableProjection();
}
}
}
});
}
}
private boolean hasSnippetsModifications() {
IDocument document = getDocument();
if (document == null) {
return false;
}
int curNbAnnotations = oldAnnotations.size();
int actualNbAnnotations = 0;
int curOffset = 0;
FindReplaceDocumentAdapter frda = new FindReplaceDocumentAdapter(document);
try {
IRegion startRegion = frda.find(curOffset, "SNIPPET_START", true, false, false, false); //$NON-NLS-1$
while (startRegion != null && startRegion.getOffset() >= curOffset) {
int startLine = document.getLineOfOffset(startRegion.getOffset());
int startOffset = document.getLineOffset(startLine);
curOffset = startOffset + document.getLineLength(startLine);
IRegion endRegion = frda.find(startRegion.getOffset(), "SNIPPET_END", true, false, false, false); //$NON-NLS-1$
if (endRegion != null) {
actualNbAnnotations++;
int endLine = document.getLineOfOffset(endRegion.getOffset());
int endOffset = document.getLineOffset(endLine);
endOffset += document.getLineLength(endLine);
curOffset = endOffset;
boolean contains = false;
String text = document.get(startOffset, endOffset - startOffset);
for (ProjectionAnnotation annotation : oldAnnotations.keySet()) {
Position pos = oldAnnotations.get(annotation);
if (annotation.getText().equals(text) && (startOffset == pos.getOffset())) {
contains = true;
}
}
if (!contains) {
return true;
}
}
if (curOffset < document.getLength()) {
startRegion = frda.find(curOffset, "SNIPPET_START", true, false, false, false); //$NON-NLS-1$
}
}
} catch (BadLocationException e) {
}
if (curNbAnnotations != actualNbAnnotations) {
return true;
}
return false;
}
private Map<ProjectionAnnotation, Position> getAllSnippetsAnnotations() {
Map<ProjectionAnnotation, Position> annotations = new HashMap<ProjectionAnnotation, Position>();
IDocument document = getDocument();
int curOffset = 0;
FindReplaceDocumentAdapter frda = new FindReplaceDocumentAdapter(document);
try {
IRegion startRegion = frda.find(curOffset, "SNIPPET_START", true, false, false, false); //$NON-NLS-1$
while (startRegion != null && startRegion.getOffset() >= curOffset) {
int startLine = document.getLineOfOffset(startRegion.getOffset());
int startOffset = document.getLineOffset(startLine);
curOffset = startOffset + document.getLineLength(startLine);
IRegion endRegion = frda.find(startRegion.getOffset(), "SNIPPET_END", true, false, false, false); //$NON-NLS-1$
if (endRegion != null) {
int endLine = document.getLineOfOffset(endRegion.getOffset());
int endOffset = document.getLineOffset(endLine);
endOffset += document.getLineLength(endLine);
curOffset = endOffset;
String text = document.get(startOffset, endOffset - startOffset);
ProjectionAnnotation annotation = new ProjectionAnnotation(true);
annotation.setText(text);
annotation.setRangeIndication(true);
annotations.put(annotation, new Position(startOffset, endOffset - startOffset));
}
if (curOffset < document.getLength()) {
startRegion = frda.find(curOffset, "SNIPPET_START", true, false, false, false); //$NON-NLS-1$
}
}
} catch (BadLocationException e) {
}
return annotations;
}
private void initializeModel() {
getSourceViewerDecorationSupport().install(JavaPlugin.getDefault().getCombinedPreferenceStore());
this.setRangeIndicator(new DefaultRangeIndicator());
IAnnotationModel model;
IDocument document;
IDocumentProvider provider = JavaPlugin.getDefault().getCompilationUnitDocumentProvider();
IEditorInput ei = new FileEditorInput(file);
try {
provider.connect(ei);
} catch (CoreException e) {
}
document = provider.getDocument(ei);
model = provider.getAnnotationModel(ei);
if (document != null) {
setDocument(document, model);
showAnnotations(model != null );
}
ProjectionSupport projectionSupport = new ProjectionSupport(this, annotationAccess, sharedColors);
projectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error"); //$NON-NLS-1$
projectionSupport.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning"); //$NON-NLS-1$
projectionSupport.setHoverControlCreator(new IInformationControlCreator() {
@Override
public IInformationControl createInformationControl(Shell shell) {
return new SourceViewerInformationControl(shell, false, SWT.LEFT_TO_RIGHT, EditorsUI.getTooltipAffordanceString());
}
});
projectionSupport.setInformationPresenterControlCreator(new IInformationControlCreator() {
@Override
public IInformationControl createInformationControl(Shell shell) {
int shellStyle = SWT.RESIZE | SWT.TOOL | SWT.LEFT_TO_RIGHT;
int style = SWT.V_SCROLL | SWT.H_SCROLL;
return new SourceViewerInformationControl(shell, true, SWT.LEFT_TO_RIGHT, null);
}
});
projectionSupport.install();
}
protected void updateVisibleRegion() {
if (this.getDocument() == null || this.viewerEndRegion == null || this.getVisibleDocument() == null) {
return;
}
final String docText = this.getDocument().get();
final int newDocLength = docText.length();
if (this.oldDocLength != newDocLength) {
final String visibleText = this.getVisibleDocument().get(); // get
final int newLength = visibleText.length();
final int newStart = newDocLength - newLength - this.viewerEndRegion.getLength();
setVisibleRegion(newStart, newLength);
this.oldDocLength = newDocLength;
}
}
private void installViewerConfiguration() {
JavaTextTools tools = JavaPlugin.getDefault().getJavaTextTools();
tools.setupJavaDocumentPartitioner(getDocument(), IJavaPartitions.JAVA_PARTITIONING);
IPreferenceStore store = JavaPlugin.getDefault().getCombinedPreferenceStore();
configure(new HydrographJavaSourceViewerConfiguration((IColorManager) sharedColors, store, this));
}
protected StyledText createTextWidget(Composite parent, int styles) {
return new ExpressionEditorStyledText(parent, SWT.BORDER| SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, this);
}
@Override
public void setVisibleRegion(int start, int length) {
viewerStartRegion = new Region(0, start);
if (getDocument() == null) {
return;
}
if (getDocument().getLength() > start) {
viewerEndRegion = new Region(start + 1 + length, getDocument().getLength() - start - length);
} else {
viewerEndRegion = new Region(start, 0);
}
super.setVisibleRegion(start, length);
}
/**
* @return decorative support for editor.
*/
public SourceViewerDecorationSupport getSourceViewerDecorationSupport() {
if (fSourceViewerDecorationSupport == null) {
fSourceViewerDecorationSupport = new SourceViewerDecorationSupport(this, fOverviewRuler, annotationAccess,
sharedColors);
configureSourceViewerDecorationSupport(fSourceViewerDecorationSupport);
}
return fSourceViewerDecorationSupport;
}
/**
* @return source viewer region.
*/
public IRegion getViewerRegion() {
if (viewerStartRegion == null) {
return new Region(0, getDocument().getLength());
}
return new Region(viewerStartRegion.getLength(), getDocument().getLength() - viewerStartRegion.getLength()
- viewerEndRegion.getLength());
}
private static SourceViewer initializeViewer(Composite composite, int styles, IDocument document,
int visibleOffset)
{
IAnnotationAccess annotationAccess = new DefaultMarkerAnnotationAccess();
@SuppressWarnings("restriction")
ISharedTextColors sharedColors = JavaPlugin.getDefault().getJavaTextTools().getColorManager();
IOverviewRuler overviewRuler = null;
IVerticalRuler verticalRuler = null;
@SuppressWarnings("restriction")
Iterator<?> e = EditorsPlugin.getDefault().getMarkerAnnotationPreferences().getAnnotationPreferences().iterator();
while (e.hasNext()) {
overviewRuler = new OverviewRuler(annotationAccess, 12, sharedColors);
AnnotationPreference preference = (AnnotationPreference) e.next();
if (preference.contributesToHeader()) {
overviewRuler.addHeaderAnnotationType(preference.getAnnotationType());
}
}
verticalRuler = new CompositeRuler(12);
SourceViewer viewer = new SourceViewer(composite, verticalRuler, overviewRuler, true, styles,
annotationAccess, sharedColors, document);
if (visibleOffset != -1) {
viewer.setVisibleRegion(visibleOffset, 0);
}
viewer.getControl().setParent(composite);
return viewer;
}
private static String getImports() {
String imports = "";
imports += "\n";
imports += "import hydrograph.engine.transformation.standardfunctions.DateFunctions;\n";
imports += "import java.text.ParseException;\n";
imports += "import java.text.SimpleDateFormat;\n";
imports += "import java.util.Date;\n";
imports += "import java.util.List;\n";
imports += "import java.math.BigDecimal;\n";
imports += "\n";
return imports;
}
/**
* @param composite parent for source viewer
* @param styles style bit for source viewer
* @return source viewer object
*/
public static SourceViewer createViewerWithVariables(Composite composite, int styles) {
IDocument document = new Document();
StringBuffer buff = new StringBuffer();
buff.append("\n package "+HYDROGRAPH_COMPILATIONUNIT_PACKAGE+";\n\n");
buff.append(getImports());
buff.append("public class " + VIEWER_CLASS_NAME + currentId + " {\n\n\n");
buff.append("\tpublic " + "String" +"myFunction(){\n");
buff.append(ExpressionEditorStyledText.RETURN_STATEMENT);
int length = buff.toString().length();
String defaultValue = " ";
buff.append(defaultValue + "\n\n\n;\t\n}\n}");
document.set(buff.toString());
return initializeViewer(composite,styles,document,length);
}
/**
* update contents for compilation unit.
*/
public void updateContents() {
if (getDocument() == null) {
return ;
}
InputStream codeStream = new ByteArrayInputStream(getDocument().get().getBytes());
try {
file = BuildExpressionEditorDataSturcture.INSTANCE.getCurrentProject().getFile( PathConstant.TEMP_BUILD_PATH_SETTINGS_FOLDER+"/hydrograph/compilationunit"+ '/' + filename);
file.setContents(codeStream, true, false, null);
initializeModel();
}
catch(Exception e){
}
}
/**
* @return compilation unit name.
*/
public static String getClassName() {
return className;
}
/**
* @return compilation unit
*/
public ICompilationUnit getCompilatioUnit() {
return compilatioUnit;
}
}