blob: 73c496eb35e531f06b6ff4fbc7fe7dfbde34a58f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 ILOG 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:
* Joel Cheuoua - Initial API and implementation
*******************************************************************************/
package org.eclipse.jet.internal.editor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jet.core.parser.ast.Comment;
import org.eclipse.jet.core.parser.ast.IncludedContent;
import org.eclipse.jet.core.parser.ast.JETASTElement;
import org.eclipse.jet.core.parser.ast.JETASTVisitor;
import org.eclipse.jet.core.parser.ast.JETCompilationUnit;
import org.eclipse.jet.core.parser.ast.JETDirective;
import org.eclipse.jet.core.parser.ast.JavaDeclaration;
import org.eclipse.jet.core.parser.ast.JavaExpression;
import org.eclipse.jet.core.parser.ast.JavaScriptlet;
import org.eclipse.jet.core.parser.ast.TextElement;
import org.eclipse.jet.core.parser.ast.XMLBodyElement;
import org.eclipse.jet.core.parser.ast.XMLBodyElementEnd;
import org.eclipse.jet.core.parser.ast.XMLEmptyElement;
import org.eclipse.jet.internal.compiler.CompilationHelper;
import org.eclipse.jet.internal.editor.configuration.JETDocumentProvider;
import org.eclipse.jet.internal.editor.configuration.JETEditorPreferenceConstants;
import org.eclipse.jet.internal.editor.configuration.JETSourceViewerConfiguration;
import org.eclipse.jet.internal.editor.configuration.JETTokenStyleManager;
import org.eclipse.jet.internal.editor.configuration.delegates.IJETDefaultTextColorerDelegate;
import org.eclipse.jet.internal.editor.outline.JETOutlinePage;
import org.eclipse.jet.internal.editor.partition.JETDocumentPartitionScanner;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.editors.text.IEncodingSupport;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.texteditor.ContentAssistAction;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.osgi.framework.Bundle;
/**
* The editor for JET templates files
*
* @author Joel
*/
public class JETTextEditor extends TextEditor {
private JETTokenStyleManager tokenStyleManager;
private JETSourceViewer sourceViewer;
private JETDocumentPartitionScanner partitionScanner;
private ProjectionSupport projectionSupport;
private IContentOutlinePage outlinePage;
private JETCompilationUnit cUnit;
private IProject project;
private Map positions;
private boolean cUnitInvalidated;
private ArrayList defaultContentColorerDelegates;
private IJETDefaultTextColorerDelegate currentDelegate;
/**
* Creates a new JET editor
*/
public JETTextEditor() {
positions = Collections.EMPTY_MAP;
IPreferenceStore preferenceStore = Activator.getDefault()
.getPreferenceStore();
JETEditorPreferenceConstants.initializeDefaultValues(preferenceStore);
setPreferenceStore(preferenceStore);
tokenStyleManager = new JETTokenStyleManager(preferenceStore);
partitionScanner = new JETDocumentPartitionScanner(this);
setSourceViewerConfiguration(new JETSourceViewerConfiguration(this));
setDocumentProvider(new JETDocumentProvider(this));
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.editors.text.TextEditor#dispose()
*/
public void dispose() {
tokenStyleManager.dispose();
super.dispose();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.texteditor.AbstractTextEditor#init(org.eclipse.ui.IEditorSite
* , org.eclipse.ui.IEditorInput)
*/
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
if (input instanceof IFileEditorInput)
project = ((IFileEditorInput) input).getFile().getProject();
super.init(site, input);
initDefaultContentColorerDelegetes();
if (!defaultContentColorerDelegates.isEmpty())
currentDelegate = (IJETDefaultTextColorerDelegate) defaultContentColorerDelegates
.get(0);
}
/**
* Set the content colorer delegate (used for syntax coloring of non JET
* text) to use.
*
* @param delegate
*/
public void setCurrentColorerDelegate(
IJETDefaultTextColorerDelegate delegate) {
currentDelegate = delegate;
// notify the damager repairers so that they recompute the partitions
// and the tokens to colorize
partitionScanner.updateJETTextContentDamagerRepairer();
// reset the text display
if (isDirty())
getSourceViewer().getDocument().set(
getSourceViewer().getDocument().get());
else
setInput(getEditorInput());
}
/**
* Returns the content colorer delegate (used for syntax coloring of non JET
* text) in use.
*
* @return the content colorer delegate
*/
public IJETDefaultTextColorerDelegate getCurrentColorerDelegate() {
return currentDelegate;
}
/**
* Returns the list of all the available (registered) content colorer
* delegates.
*
* @return
*/
public IJETDefaultTextColorerDelegate[] getColorerDelegates() {
IJETDefaultTextColorerDelegate results[] = new IJETDefaultTextColorerDelegate[defaultContentColorerDelegates
.size()];
defaultContentColorerDelegates.toArray(results);
return results;
}
/**
* Compute the list of the registered content colorer delegates.
*/
private void initDefaultContentColorerDelegetes() {
defaultContentColorerDelegates = new ArrayList();
IExtensionRegistry registry = Platform.getExtensionRegistry();
IConfigurationElement elements[] = registry
.getConfigurationElementsFor("org.eclipse.jet.internal.editor",
"defaultContentColorerDelegates");
for (int i = 0; i < elements.length; i++) {
IConfigurationElement element = elements[i];
Bundle bundle = Platform
.getBundle(element.getNamespaceIdentifier());
String delegateClass = element.getAttribute("delegateClass");
IJETDefaultTextColorerDelegate delegate = null;
try {
Class clazz = bundle.loadClass(delegateClass);
Class argTypes[] = { JETTextEditor.class };
Constructor ctorWithTextEditor = null;
try {
ctorWithTextEditor = clazz.getConstructor(argTypes);
} catch (SecurityException e) {
Activator.log(e);
} catch (NoSuchMethodException e) {
Activator.log(e);
}
if (ctorWithTextEditor != null)
delegate = (IJETDefaultTextColorerDelegate) ctorWithTextEditor
.newInstance(new Object[] { this });
else
delegate = (IJETDefaultTextColorerDelegate) clazz
.newInstance();
defaultContentColorerDelegates.add(delegate);
} catch (ClassNotFoundException e) {
Activator.log(e);
} catch (InstantiationException e) {
Activator.log(e);
} catch (IllegalAccessException e) {
Activator.log(e);
} catch (IllegalArgumentException e) {
Activator.log(e);
} catch (InvocationTargetException e) {
Activator.log(e);
}
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createSourceViewer
* (org.eclipse.swt.widgets.Composite,
* org.eclipse.jface.text.source.IVerticalRuler, int)
*/
protected ISourceViewer createSourceViewer(Composite parent,
IVerticalRuler ruler, int styles) {
fAnnotationAccess = getAnnotationAccess();
fOverviewRuler = createOverviewRuler(getSharedColors());
sourceViewer = new JETSourceViewer(parent, ruler, getOverviewRuler(),
isOverviewRulerVisible(), styles, getPreferenceStore());
getSourceViewerDecorationSupport(sourceViewer);
projectionSupport = new ProjectionSupport(sourceViewer,
getAnnotationAccess(), getSharedColors());
projectionSupport
.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.error");
projectionSupport
.addSummarizableAnnotationType("org.eclipse.ui.workbench.texteditor.warning");
projectionSupport
.setHoverControlCreator(new IInformationControlCreator() {
public IInformationControl createInformationControl(
Shell shell) {
return new DefaultInformationControl(shell, true);
// return new DefaultInformationControl(shell,
// getOrientation(), 0, new HTMLTextPresenter());
}
});
projectionSupport.install();
return sourceViewer;
}
/**
* Return the editor's source viewer. This method exist to make this viewer
* accessible to other classes.
*
* @return the editor's source viewer
*/
public ISourceViewer getEditorSourceViewer() {
return sourceViewer;
}
/**
* Returns the editor's preference store. This method exist to make the
* preference store accessible to other classes.
*
* @return the editor's preference store
*/
public IPreferenceStore getEditorPreferenceStore() {
return super.getPreferenceStore();
}
public JETDocumentPartitionScanner getPartitionScanner() {
return partitionScanner;
}
public JETTokenStyleManager getTokenStyleManager() {
return tokenStyleManager;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ui.texteditor.StatusTextEditor#handleEditorInputChanged()
*/
protected void handleEditorInputChanged() {
super.handleEditorInputChanged();
handleDocumentChange(null);
}
public void handleDocumentChange(DocumentEvent e) {
cUnitInvalidated = true;
if (outlinePage != null && outlinePage.getControl().isVisible())
((JETOutlinePage) outlinePage).setInput();
}
public boolean isCUnitInvalidated() {
return cUnitInvalidated;
}
public CompilationHelper compilationHelper() {
CompilationHelper jetCompilationHelper = new CompilationHelper(project);
return jetCompilationHelper;
}
/**
* Compute and retrun the compilation unit of this document
*
* @return the computed (or recomputed) compilation unit for this template
*/
public JETCompilationUnit requestCompilationUnit() {
if (cUnit == null || cUnitInvalidated) {
IEditorInput editorInput = getEditorInput();
String templatePath = editorInput.getName();
if (editorInput instanceof IFileEditorInput)
templatePath = ((IFileEditorInput) editorInput).getFile()
.getProjectRelativePath().toString();
if (sourceViewer == null) { // sourceViewer is null ... we can parse
// from the file directly
cUnit = compilationHelper().getAST(templatePath);
} else {
cUnit = compilationHelper().getASTFromSource(
sourceViewer.getDocument().get(), templatePath);
}
PositionsCollector collector = new PositionsCollector();
cUnit.accept(collector);
positions = collector.getPositions();
cUnitInvalidated = false;
}
return cUnit;
}
public JETASTElement getASTElement(int offset) {
requestCompilationUnit();
return getCachedASTElement(offset);
}
public JETASTElement getCachedASTElement(int offset) {
for (Iterator iterator = positions.keySet().iterator(); iterator
.hasNext();) {
Position pos = (Position) iterator.next();
if (pos.offset <= offset && pos.length + pos.offset > offset)
return (JETASTElement) positions.get(pos);
}
return null;
}
public IJavaProject getJavaProject() {
return JavaCore.create(project);
}
protected void createActions() {
super.createActions();
ResourceBundle resourceBundle = ResourceBundle
.getBundle("org.eclipse.jet.internal.editor.i18n.TextEditorMessages");
Action action = new ContentAssistAction(resourceBundle,
"ContentAssistProposal.", this);
action
.setActionDefinitionId("org.eclipse.ui.edit.text.contentAssist.proposals");
setAction("ContentAssistProposal", action);
markAsStateDependentAction("ContentAssistProposal", true);
}
protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
super.handlePreferenceStoreChanged(event);
if (event.getNewValue() instanceof RGB)
getTokenStyleManager().bindColor(event.getProperty(),
(RGB) event.getNewValue());
}
public Object getAdapter(Class required) {
if (IContentOutlinePage.class.equals(required)) {
if (outlinePage == null)
outlinePage = createOutlinePage();
return outlinePage;
}
if (IEncodingSupport.class.equals(required))
return fEncodingSupport;
if (projectionSupport != null) {
Object adapter = projectionSupport.getAdapter(getSourceViewer(),
required);
if (adapter != null)
return adapter;
}
return super.getAdapter(required);
}
public void editorContextMenuAboutToShow(IMenuManager menu) {
super.editorContextMenuAboutToShow(menu);
addAction(menu, "settings", "setDelegateColorerActionID");
}
protected IContentOutlinePage createOutlinePage() {
JETOutlinePage page = new JETOutlinePage(this);
page.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
handleContentOutlineSelection(event.getSelection());
}
});
return page;
}
protected void handleContentOutlineSelection(ISelection selection) {
IStructuredSelection ssel = (IStructuredSelection) selection;
Object firstElement = ssel.getFirstElement();
if (firstElement instanceof JETASTElement) {
JETASTElement astElement = (JETASTElement) firstElement;
int start = astElement.getStart();
int end = astElement.getEnd();
int length = end - start;
if (astElement instanceof TextElement) {
if (astElement.getPrevElement() != null)
start = astElement.getPrevElement().getEnd();
else
start = 0;
length = ((TextElement) astElement).getText().length;
}
sourceViewer.setSelectedRange(start, length);
sourceViewer.revealRange(start, length);
}
}
private static class PositionsCollector extends JETASTVisitor {
private Map positions;
private PositionsCollector() {
positions = new HashMap();
}
public Map getPositions() {
return positions;
}
public boolean visit(JavaDeclaration declaration) {
return matchOffset(declaration);
}
public boolean visit(JETDirective directive) {
return matchOffset(directive);
}
public boolean visit(JavaExpression expression) {
return matchOffset(expression);
}
public boolean visit(IncludedContent content) {
// don't process include content
return false;
}
public boolean visit(JavaScriptlet scriptlet) {
return matchOffset(scriptlet);
}
public boolean visit(XMLEmptyElement xmlEmptyElement) {
return matchOffset(xmlEmptyElement);
}
public boolean visit(XMLBodyElement xmlBodyElement) {
return matchOffset(xmlBodyElement);
}
public boolean visit(XMLBodyElementEnd xmlBodyElementEnd) {
return matchOffset(xmlBodyElementEnd);
}
public boolean visit(Comment comment) {
return matchOffset(comment);
}
public boolean matchOffset(JETASTElement astElement) {
int start = astElement.getStart();
if (start < 0) {
return false;
}
int end = astElement.getEnd();
int length = end - Math.abs(start);
positions.put(new Position(start, length), astElement);
return true;
}
}
}