| /******************************************************************************* |
| * Copyright (c) 2005, 2007 IBM Corporation 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 |
| * |
| |
| *******************************************************************************/ |
| package org.eclipse.dltk.javascript.internal.ui.templates; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.javascript.ast.MultiLineComment; |
| import org.eclipse.dltk.javascript.ast.PropertyInitializer; |
| import org.eclipse.dltk.javascript.core.JavaScriptNature; |
| import org.eclipse.dltk.javascript.internal.core.codeassist.JavaScriptCompletionUtil; |
| import org.eclipse.dltk.javascript.internal.core.codeassist.JavaScriptCompletionUtil.ExpressionContext; |
| import org.eclipse.dltk.javascript.internal.core.codeassist.JavaScriptCompletionUtil.ExpressionType; |
| import org.eclipse.dltk.javascript.internal.ui.JavaScriptUI; |
| import org.eclipse.dltk.ui.formatter.FormatterException; |
| import org.eclipse.dltk.ui.formatter.FormatterSyntaxProblemException; |
| import org.eclipse.dltk.ui.formatter.IScriptFormatter; |
| import org.eclipse.dltk.ui.formatter.IScriptFormatterFactory; |
| import org.eclipse.dltk.ui.formatter.ScriptFormatterManager; |
| import org.eclipse.dltk.ui.templates.ScriptTemplateContext; |
| import org.eclipse.dltk.ui.text.DocumentUtils; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.Position; |
| import org.eclipse.jface.text.TextUtilities; |
| 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.TemplateContextType; |
| import org.eclipse.jface.text.templates.TemplateException; |
| import org.eclipse.jface.text.templates.TemplateVariable; |
| import org.eclipse.text.edits.TextEdit; |
| |
| public class JavaScriptTemplateContext extends ScriptTemplateContext { |
| |
| public JavaScriptTemplateContext(TemplateContextType type, |
| IDocument document, int completionOffset, int completionLength, |
| ISourceModule sourceModule) { |
| super(type, document, completionOffset, completionLength, sourceModule); |
| } |
| |
| public JavaScriptTemplateContext(TemplateContextType type, |
| IDocument document, Position position, ISourceModule sourceModule) { |
| super(type, document, position, sourceModule); |
| } |
| |
| @Override |
| public TemplateBuffer evaluate(Template template) |
| throws BadLocationException, TemplateException { |
| if (!canEvaluate(template)) { |
| return null; |
| } |
| final IScriptFormatterFactory factory = ScriptFormatterManager |
| .getSelected(JavaScriptNature.NATURE_ID, getSourceModule() |
| .getScriptProject().getProject()); |
| if (factory != null) { |
| final IScriptFormatter formatter = factory.createFormatter( |
| TextUtilities.getDefaultLineDelimiter(getDocument()), |
| factory.retrievePreferences(getPreferences())); |
| try { |
| final Map<String, String> rememberedVariables = new HashMap<String, String>(); |
| final String encoded = encodeVariables(template.getPattern(), |
| rememberedVariables); |
| final TextEdit edit = formatter.format(encoded, 0, |
| encoded.length(), 0); |
| if (edit != null) { |
| final Document document = new Document(encoded); |
| edit.apply(document); |
| template = new Template(template.getName(), |
| template.getDescription(), |
| template.getContextTypeId(), restoreVariables( |
| document.get(), rememberedVariables), |
| template.isAutoInsertable()); |
| } |
| } catch (FormatterSyntaxProblemException e) { |
| // ignore & & fall thru |
| } catch (FormatterException e) { |
| JavaScriptUI.log(e); |
| } |
| } |
| final TemplateBuffer templateBuffer = super.evaluate(template); |
| if (templateBuffer != null |
| && !isReadOnly() |
| && templateBuffer.getString().startsWith( |
| MultiLineComment.JSDOC_PREFIX)) { |
| final ExpressionContext expressionContext = JavaScriptCompletionUtil |
| .evaluateExpressionContext(getSourceModule(), |
| DocumentUtils.asCharSequence(getDocument()), |
| getEnd()); |
| if (expressionContext != null |
| && expressionContext.expressionType == ExpressionType.PROPERTY_INITIALIZER_VALUE) { |
| final String replacement = templateBuffer.getString(); |
| int docEnd = replacement |
| .indexOf(JavaScriptTemplateProposal.C_END); |
| if (docEnd > 0) { |
| docEnd += JavaScriptTemplateProposal.C_END.length(); |
| final PropertyInitializer propertyInitializer = (PropertyInitializer) expressionContext.node |
| .getParent(); |
| |
| final String doc = replacement.substring(0, docEnd) |
| + TextUtilities |
| .getDefaultLineDelimiter(getDocument()) |
| + calculateIndent(getDocument(), |
| propertyInitializer.start()); |
| |
| while (docEnd + 1 < replacement.length() |
| && Character.isWhitespace(replacement |
| .charAt(docEnd))) { |
| ++docEnd; |
| } |
| if (propertyInitializer.getName().getDocumentation() != null) { |
| templateBuffer.setContent( |
| replacement.substring(docEnd), |
| filterTemplateVariables( |
| templateBuffer.getVariables(), docEnd)); |
| } else { |
| final int docOffset = getStart() |
| - propertyInitializer.start() + doc.length(); |
| getDocument().replace(propertyInitializer.start(), 0, |
| doc); |
| setCompletionOffset(getCompletionOffset() |
| + doc.length()); |
| for (TemplateVariable variable : templateBuffer |
| .getVariables()) { |
| int[] offsets = variable.getOffsets(); |
| for (int i = 0; i < offsets.length; ++i) { |
| if (offsets[i] < docEnd) { |
| offsets[i] -= docOffset; |
| } else { |
| offsets[i] -= docEnd; |
| } |
| } |
| } |
| templateBuffer.setContent( |
| replacement.substring(docEnd), |
| templateBuffer.getVariables()); |
| } |
| } |
| } |
| } |
| return templateBuffer; |
| } |
| |
| private TemplateVariable[] filterTemplateVariables( |
| TemplateVariable[] variables, int offset) { |
| List<TemplateVariable> result = new ArrayList<TemplateVariable>( |
| variables.length); |
| for (TemplateVariable variable : variables) { |
| int[] offsets = filterOffsets(variable.getOffsets(), offset); |
| if (offsets != null) { |
| variable.setOffsets(offsets); |
| result.add(variable); |
| } |
| } |
| return result.toArray(new TemplateVariable[result.size()]); |
| } |
| |
| private int[] filterOffsets(int[] offsets, int offset) { |
| int[] result = new int[offset]; |
| int count = 0; |
| for (int i = 0; i < offsets.length; ++i) { |
| if (offsets[i] >= offset) { |
| result[count++] -= offsets[i] - offset; |
| } |
| } |
| if (count == 0) { |
| return null; |
| } else if (count == result.length) { |
| return result; |
| } else { |
| int[] newResult = new int[count]; |
| System.arraycopy(result, 0, newResult, 0, count); |
| return newResult; |
| } |
| } |
| |
| /** |
| * Temporary replace variables with unique keys, so variables would not |
| * cause parsing errors during formatting. |
| * |
| * @param content |
| * @param variables |
| * @return |
| */ |
| private static String encodeVariables(String content, |
| Map<String, String> variables) { |
| final StringBuilder bf = new StringBuilder(content.length() * 2); |
| boolean in = false; |
| final String prefix = "specialSecret12435Id"; |
| int r = 0; |
| int pos = -1; |
| for (int a = 0; a < content.length(); a++) { |
| char c = content.charAt(a); |
| if (c == '$') { |
| if (a < content.length() - 1) |
| if (content.charAt(a + 1) == '{') { |
| in = true; |
| pos = a; |
| } |
| } else if (in) { |
| if (c == '}') { |
| String variableValue = content.substring(pos, a + 1); |
| String variableKey = prefix + r++; |
| if (variableValue.equals("${" |
| + GlobalTemplateVariables.Cursor.NAME + "}")) { |
| variableKey = "/*" + variableKey + "*/"; |
| } |
| bf.append(variableKey); |
| variables.put(variableKey, variableValue); |
| in = false; |
| } |
| } else if (!in) { |
| bf.append(c); |
| } |
| } |
| return bf.toString(); |
| } |
| |
| private static String restoreVariables(String value, |
| Map<String, String> variables) { |
| String formatted = value; |
| for (Map.Entry<String, String> entry : variables.entrySet()) { |
| formatted = replaceSeq(formatted, entry.getKey(), entry.getValue()); |
| } |
| return formatted; |
| } |
| |
| private static String replaceSeq(String sq, String target, |
| String replacement) { |
| // return Pattern.compile(target.toString(), Pattern.LITERAL).matcher( |
| // sq).replaceAll(Matcher.quoteReplacement(replacement.toString())); |
| int indexOf = sq.indexOf(target); |
| while (indexOf != -1) { |
| sq = sq.substring(0, indexOf) + replacement |
| + sq.substring(indexOf + target.length()); |
| indexOf = sq.indexOf(target); |
| } |
| return sq; |
| } |
| } |