--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 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 net.sourceforge.phpdt.internal.ui.text.template.contentassist;
+
+import net.sourceforge.phpdt.internal.corext.template.php.CompilationUnitContext;
+import net.sourceforge.phpdt.internal.corext.template.php.JavaContext;
+import net.sourceforge.phpdt.internal.ui.text.java.IPHPCompletionProposal;
+import net.sourceforge.phpdt.internal.ui.util.ExceptionHandler;
+import net.sourceforge.phpeclipse.PHPeclipsePlugin;
+import net.sourceforge.phpeclipse.phpeditor.EditorHighlightingSynchronizer;
+import net.sourceforge.phpeclipse.phpeditor.PHPEditor;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.MessageDialog;
+//incastrix
+//import org.eclipse.jface.text.Assert;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.link.ILinkedModeListener;
+import org.eclipse.jface.text.link.LinkedModeModel;
+import org.eclipse.jface.text.link.LinkedModeUI;
+import org.eclipse.jface.text.link.LinkedPosition;
+import org.eclipse.jface.text.link.LinkedPositionGroup;
+import org.eclipse.jface.text.link.ProposalPosition;
+import org.eclipse.jface.text.templates.DocumentTemplateContext;
+import org.eclipse.jface.text.templates.GlobalTemplateVariables;
+import org.eclipse.jface.text.templates.Template;
+import org.eclipse.jface.text.templates.TemplateBuffer;
+import org.eclipse.jface.text.templates.TemplateContext;
+import org.eclipse.jface.text.templates.TemplateException;
+import org.eclipse.jface.text.templates.TemplateVariable;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.texteditor.link.EditorLinkedModeUI;
+
+/**
+ * A template proposal.
+ */
+public class TemplateProposal implements IPHPCompletionProposal,
+ ICompletionProposalExtension2, ICompletionProposalExtension3 {
+
+ private final Template fTemplate;
+
+ private final TemplateContext fContext;
+
+ private final Image fImage;
+
+ private IRegion fRegion;
+
+ private int fRelevance;
+
+ private IRegion fSelectedRegion; // initialized by apply()
+
+ private String fDisplayString;
+
+ /**
+ * Creates a template proposal with a template and its context.
+ *
+ * @param template
+ * the template
+ * @param context
+ * the context in which the template was requested
+ * @param region
+ * the region this proposal applies to
+ * @param image
+ * the icon of the proposal
+ */
+ public TemplateProposal(Template template, TemplateContext context,
+ IRegion region, Image image) {
+ Assert.isNotNull(template);
+ Assert.isNotNull(context);
+ Assert.isNotNull(region);
+
+ fTemplate = template;
+ fContext = context;
+ fImage = image;
+ fRegion = region;
+
+ fDisplayString = null;
+
+ if (context instanceof JavaContext) {
+ switch (((JavaContext) context).getCharacterBeforeStart()) {
+ // high relevance after whitespace
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ fRelevance = 90;
+ break;
+ default:
+ fRelevance = 0;
+ }
+ } else {
+ fRelevance = 90;
+ }
+ }
+
+ /*
+ * @see ICompletionProposal#apply(IDocument)
+ */
+ public final void apply(IDocument document) {
+ // not called anymore
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#apply(org.eclipse.jface.text.ITextViewer,
+ * char, int, int)
+ */
+ public void apply(ITextViewer viewer, char trigger, int stateMask,
+ int offset) {
+
+ try {
+
+ fContext.setReadOnly(false);
+ TemplateBuffer templateBuffer;
+ try {
+ templateBuffer = fContext.evaluate(fTemplate);
+ } catch (TemplateException e1) {
+ fSelectedRegion = fRegion;
+ return;
+ }
+
+ int start = getReplaceOffset();
+ int end = getReplaceEndOffset();
+ end = Math.max(end, offset);
+
+ // insert template string
+ IDocument document = viewer.getDocument();
+ String templateString = templateBuffer.getString();
+ document.replace(start, end - start, templateString);
+
+ // translate positions
+ LinkedModeModel model = new LinkedModeModel();
+ TemplateVariable[] variables = templateBuffer.getVariables();
+
+ MultiVariableGuess guess = fContext instanceof CompilationUnitContext ? ((CompilationUnitContext) fContext)
+ .getMultiVariableGuess()
+ : null;
+
+ boolean hasPositions = false;
+ for (int i = 0; i != variables.length; i++) {
+ TemplateVariable variable = variables[i];
+
+ if (variable.isUnambiguous())
+ continue;
+
+ LinkedPositionGroup group = new LinkedPositionGroup();
+
+ int[] offsets = variable.getOffsets();
+ int length = variable.getLength();
+
+ LinkedPosition first;
+ if (guess != null && variable instanceof MultiVariable) {
+ first = new VariablePosition(document, offsets[0] + start,
+ length, guess, (MultiVariable) variable);
+ guess.addSlave((VariablePosition) first);
+ } else {
+ String[] values = variable.getValues();
+ ICompletionProposal[] proposals = new ICompletionProposal[values.length];
+ for (int j = 0; j < values.length; j++) {
+ ensurePositionCategoryInstalled(document, model);
+ Position pos = new Position(offsets[0] + start, length);
+ document.addPosition(getCategory(), pos);
+ proposals[j] = new PositionBasedCompletionProposal(
+ values[j], pos, length);
+ }
+
+ if (proposals.length > 1)
+ first = new ProposalPosition(document, offsets[0]
+ + start, length, proposals);
+ else
+ first = new LinkedPosition(document,
+ offsets[0] + start, length);
+ }
+
+ for (int j = 0; j != offsets.length; j++)
+ if (j == 0)
+ group.addPosition(first);
+ else
+ group.addPosition(new LinkedPosition(document,
+ offsets[j] + start, length));
+
+ model.addGroup(group);
+ hasPositions = true;
+ }
+
+ if (hasPositions) {
+ model.forceInstall();
+ PHPEditor editor = getJavaEditor();
+ if (editor != null) {
+ model
+ .addLinkingListener(new EditorHighlightingSynchronizer(
+ editor));
+ }
+
+ LinkedModeUI ui = new EditorLinkedModeUI(model, viewer);
+ ui.setExitPosition(viewer, getCaretOffset(templateBuffer)
+ + start, 0, Integer.MAX_VALUE);
+ ui.enter();
+
+ fSelectedRegion = ui.getSelectedRegion();
+ } else
+ fSelectedRegion = new Region(getCaretOffset(templateBuffer)
+ + start, 0);
+
+ } catch (BadLocationException e) {
+ PHPeclipsePlugin.log(e);
+ openErrorDialog(viewer.getTextWidget().getShell(), e);
+ fSelectedRegion = fRegion;
+ } catch (BadPositionCategoryException e) {
+ PHPeclipsePlugin.log(e);
+ openErrorDialog(viewer.getTextWidget().getShell(), e);
+ fSelectedRegion = fRegion;
+ }
+
+ }
+
+ /**
+ * Returns the currently active java editor, or <code>null</code> if it
+ * cannot be determined.
+ *
+ * @return the currently active java editor, or <code>null</code>
+ */
+ private PHPEditor getJavaEditor() {
+ IEditorPart part = PHPeclipsePlugin.getActivePage().getActiveEditor();
+ if (part instanceof PHPEditor)
+ return (PHPEditor) part;
+ else
+ return null;
+ }
+
+ /**
+ * Returns the offset of the range in the document that will be replaced by
+ * applying this template.
+ *
+ * @return the offset of the range in the document that will be replaced by
+ * applying this template
+ */
+ private int getReplaceOffset() {
+ int start;
+ if (fContext instanceof DocumentTemplateContext) {
+ DocumentTemplateContext docContext = (DocumentTemplateContext) fContext;
+ start = docContext.getStart();
+ } else {
+ start = fRegion.getOffset();
+ }
+ return start;
+ }
+
+ /**
+ * Returns the end offset of the range in the document that will be replaced
+ * by applying this template.
+ *
+ * @return the end offset of the range in the document that will be replaced
+ * by applying this template
+ */
+ private int getReplaceEndOffset() {
+ int end;
+ if (fContext instanceof DocumentTemplateContext) {
+ DocumentTemplateContext docContext = (DocumentTemplateContext) fContext;
+ end = docContext.getEnd();
+ } else {
+ end = fRegion.getOffset() + fRegion.getLength();
+ }
+ return end;
+ }
+
+ private void ensurePositionCategoryInstalled(final IDocument document,
+ LinkedModeModel model) {
+ if (!document.containsPositionCategory(getCategory())) {
+ document.addPositionCategory(getCategory());
+ final InclusivePositionUpdater updater = new InclusivePositionUpdater(
+ getCategory());
+ document.addPositionUpdater(updater);
+
+ model.addLinkingListener(new ILinkedModeListener() {
+
+ /*
+ * @see org.eclipse.jface.text.link.ILinkedModeListener#left(org.eclipse.jface.text.link.LinkedModeModel,
+ * int)
+ */
+ public void left(LinkedModeModel environment, int flags) {
+ try {
+ document.removePositionCategory(getCategory());
+ } catch (BadPositionCategoryException e) {
+ // ignore
+ }
+ document.removePositionUpdater(updater);
+ }
+
+ public void suspend(LinkedModeModel environment) {
+ }
+
+ public void resume(LinkedModeModel environment, int flags) {
+ }
+ });
+ }
+ }
+
+ private String getCategory() {
+ return "TemplateProposalCategory_" + toString(); //$NON-NLS-1$
+ }
+
+ private int getCaretOffset(TemplateBuffer buffer) {
+
+ TemplateVariable[] variables = buffer.getVariables();
+ for (int i = 0; i != variables.length; i++) {
+ TemplateVariable variable = variables[i];
+ if (variable.getType().equals(GlobalTemplateVariables.Cursor.NAME))
+ return variable.getOffsets()[0];
+ }
+
+ return buffer.getString().length();
+ }
+
+ /*
+ * @see ICompletionProposal#getSelection(IDocument)
+ */
+ public Point getSelection(IDocument document) {
+ return new Point(fSelectedRegion.getOffset(), fSelectedRegion
+ .getLength());
+ }
+
+ /*
+ * @see ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ try {
+ fContext.setReadOnly(true);
+ TemplateBuffer templateBuffer;
+ try {
+ templateBuffer = fContext.evaluate(fTemplate);
+ } catch (TemplateException e1) {
+ return null;
+ }
+
+ return templateBuffer.getString();
+
+ } catch (BadLocationException e) {
+ handleException(PHPeclipsePlugin.getActiveWorkbenchShell(),
+ new CoreException(new Status(IStatus.ERROR,
+ PHPeclipsePlugin.getPluginId(), IStatus.OK, "", e))); //$NON-NLS-1$
+ return null;
+ }
+ }
+
+ /*
+ * @see ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ if (fDisplayString == null) {
+ fDisplayString = fTemplate.getName()
+ + TemplateContentAssistMessages
+ .getString("TemplateProposal.delimiter") + fTemplate.getDescription(); //$NON-NLS-1$
+ }
+ return fDisplayString;
+ }
+
+// public void setDisplayString(String displayString) {
+// fDisplayString = displayString;
+// }
+
+ /*
+ * @see ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return fImage;
+ }
+
+ /*
+ * @see ICompletionProposal#getContextInformation()
+ */
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ private void openErrorDialog(Shell shell, Exception e) {
+ MessageDialog.openError(shell, TemplateContentAssistMessages
+ .getString("TemplateEvaluator.error.title"), e.getMessage()); //$NON-NLS-1$
+ }
+
+ private void handleException(Shell shell, CoreException e) {
+ ExceptionHandler.handle(e, shell, TemplateContentAssistMessages
+ .getString("TemplateEvaluator.error.title"), null); //$NON-NLS-1$
+ }
+
+ /*
+ * @see IJavaCompletionProposal#getRelevance()
+ */
+ public int getRelevance() {
+ return fRelevance;
+ }
+
+// public void setRelevance(int relevance) {
+// fRelevance = relevance;
+// }
+
+// public Template getTemplate() {
+// return fTemplate;
+// }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getInformationControlCreator()
+ */
+ public IInformationControlCreator getInformationControlCreator() {
+ return new TemplateInformationControlCreator();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#selected(org.eclipse.jface.text.ITextViewer,
+ * boolean)
+ */
+ public void selected(ITextViewer viewer, boolean smartToggle) {
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#unselected(org.eclipse.jface.text.ITextViewer)
+ */
+ public void unselected(ITextViewer viewer) {
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension2#validate(org.eclipse.jface.text.IDocument,
+ * int, org.eclipse.jface.text.DocumentEvent)
+ */
+ public boolean validate(IDocument document, int offset, DocumentEvent event) {
+ try {
+ int replaceOffset = getReplaceOffset();
+ if (offset >= replaceOffset) {
+ String content = document.get(replaceOffset, offset
+ - replaceOffset);
+ return fTemplate.getName().startsWith(content);
+ }
+ } catch (BadLocationException e) {
+ // concurrent modification - ignore
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementString()
+ */
+ public CharSequence getPrefixCompletionText(IDocument document,
+ int completionOffset) {
+ return fTemplate.getName();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension3#getReplacementOffset()
+ */
+ public int getPrefixCompletionStart(IDocument document, int completionOffset) {
+ return getReplaceOffset();
+ }
+}