/******************************************************************************* | |
* 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.net.URI; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import org.eclipse.core.resources.IFile; | |
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.IProblemRequestor; | |
import org.eclipse.jdt.core.JavaModelException; | |
import org.eclipse.jdt.core.WorkingCopyOwner; | |
import org.eclipse.jdt.core.compiler.IProblem; | |
import org.eclipse.jdt.core.eval.IEvaluationContext; | |
import org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal; | |
import org.eclipse.jdt.ui.text.java.CompletionProposalCollector; | |
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; | |
import org.eclipse.jet.compiler.CompileOptionsManager; | |
import org.eclipse.jet.core.compiler.JETCompilerOptions; | |
import org.eclipse.jet.core.parser.ProblemSeverity; | |
import org.eclipse.jet.core.parser.ast.JETASTElement; | |
import org.eclipse.jet.core.parser.ast.JETCompilationUnit; | |
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.Problem; | |
import org.eclipse.jface.text.BadLocationException; | |
import org.eclipse.jface.text.Document; | |
import org.eclipse.jface.text.IDocument; | |
import org.eclipse.jface.text.ITextViewer; | |
import org.eclipse.jface.text.Position; | |
import org.eclipse.swt.graphics.Point; | |
import org.eclipse.ui.IFileEditorInput; | |
/** | |
* Helper class for code completion and JET compilation unit processing | |
* purposespurposes | |
* | |
* @author Joel | |
* | |
*/ | |
public class JETEditorHelper { | |
/** | |
* Problem requestor for java expressions of the JET template | |
* | |
* @author Joel | |
* | |
*/ | |
private static final class JETJavaContentProblemRequestor implements IProblemRequestor { | |
/** | |
* The JET problems computed from the java problems | |
*/ | |
private List problems; | |
/** | |
* The problems collected from the compiled java source | |
*/ | |
private List javaProblems; | |
/** | |
* The jet source | |
*/ | |
private IDocument jetDocument; | |
private Map mappingPositions; | |
private JETJavaContentProblemRequestor(List problems, Map mappings, IDocument jetDocument) { | |
this.problems = problems; | |
mappingPositions = mappings; | |
javaProblems = new ArrayList(); | |
this.jetDocument = jetDocument; | |
} | |
/** | |
* @return | |
*/ | |
public List getProblems() { | |
return problems; | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.jdt.core.IProblemRequestor#acceptProblem(org.eclipse. | |
* jdt.core.compiler.IProblem) | |
*/ | |
public void acceptProblem(IProblem problem) { | |
javaProblems.add(problem); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.eclipse.jdt.core.IProblemRequestor#beginReporting() | |
*/ | |
public void beginReporting() { | |
javaProblems.clear(); | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.eclipse.jdt.core.IProblemRequestor#endReporting() | |
*/ | |
public void endReporting() { | |
Problem problem; | |
// walk through the java problems collected from the generated java | |
// source and create the | |
// corresponding JET problems in the JET source using the position | |
// mappings | |
for (Iterator iter = javaProblems.iterator(); iter.hasNext();) { | |
IProblem javaProblem = (IProblem) iter.next(); | |
int javaProblemOffset = javaProblem.getSourceStart(); | |
int problemLength = (javaProblem.getSourceEnd() - javaProblemOffset) + 1; | |
// Retrieve the corresponding JET AST Element and its position | |
// in the JET source | |
JETASTElement astElement = getElementForJavaOffset(javaProblemOffset); | |
Position pos = (Position) mappingPositions.get(astElement); | |
// jetOffset = | |
// start position of the AST element in JET Code + | |
// offset till the position delimited by the javaProblemOffset | |
// The offset till the position delimited by the | |
// javaProblemOffset | |
// depends on the starting offset of the java content in the JET | |
// AST element. | |
// This is due to the fact that the JET AST element first | |
// position does not represents the starting offset of | |
// offset of its java content. | |
int astElementJavaContentStart = pos == null ? -1 : pos.getOffset(); | |
int jetOffset = pos == null ? 0 : JETEditorHelper.getASTElementContentStart(astElement, jetDocument) + (javaProblemOffset - astElementJavaContentStart); | |
// Create the problem at the specified location | |
URI baseLocation = URI.create(""); | |
problem = new Problem(baseLocation, "", javaProblem.isWarning() ? ProblemSeverity.WARNING : ProblemSeverity.ERROR, -1, javaProblem.getMessage(), javaProblem.getArguments(), jetOffset, jetOffset + problemLength, -1, -1); | |
// add the problem to the problems list | |
problems.add(problem); | |
} | |
} | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.eclipse.jdt.core.IProblemRequestor#isActive() | |
*/ | |
public boolean isActive() { | |
return true; | |
} | |
/** | |
* Find the JET AST element that matches the given java offset in the | |
* generated java template source | |
* | |
* @param javaOffset | |
* @return | |
*/ | |
private JETASTElement getElementForJavaOffset(int javaOffset) { | |
for (Iterator iterator = mappingPositions.keySet().iterator(); iterator.hasNext();) { | |
JETASTElement element = (JETASTElement) iterator.next(); | |
Position pos = (Position) mappingPositions.get(element); | |
if (pos.getOffset() <= javaOffset && javaOffset < pos.getOffset() + pos.getLength()) | |
return element; | |
} | |
return null; | |
} | |
} // End class JETJavaContentProblemRequestor | |
/** | |
* @param astElement | |
* @param jetDocument | |
* @return | |
*/ | |
public static int getASTElementContentStart(JETASTElement astElement, IDocument jetDocument) { | |
int jetStart = astElement.getStart(); | |
if (astElement instanceof JavaDeclaration) | |
jetStart += 3; | |
if ((astElement instanceof JavaExpression) || (astElement instanceof JavaScriptlet)) | |
try { | |
String jetSnippet = jetDocument.get(jetStart, astElement.getEnd() - jetStart); | |
String javaContent = (astElement instanceof JavaExpression) ? ((JavaExpression) astElement).getJavaContent() : ((JavaScriptlet) astElement).getJavaContent(); | |
int idx = jetSnippet.indexOf(javaContent); | |
jetStart += idx; | |
} catch (BadLocationException e) { | |
Activator.log(e); | |
} | |
return jetStart; | |
} | |
public static boolean checkOffsetPositions(String javaSource, String jetSource, int jetOffset, int javaOffset, int checkDepth) { | |
boolean result = false; | |
String javaCheckString = javaSource.substring(javaOffset, Math.min(javaSource.length(), javaOffset + checkDepth)); | |
String jetCheckString = jetSource.substring(jetOffset, Math.min(jetSource.length(), jetOffset + checkDepth)); | |
result = javaCheckString.compareTo(jetCheckString) == 0; | |
if (!result) { | |
System.err.println("position matching error"); | |
System.err.println("JET Check String : " + jetCheckString); | |
System.err.println("Java Check String : " + javaCheckString); | |
} | |
return result; | |
} | |
/** | |
* @param jetEditor | |
* @param cu | |
* @param jetDocument | |
* @return | |
*/ | |
public static List evaluateProblems(JETTextEditor jetEditor, IDocument jetDocument) { | |
JETCompilationUnit cu = jetEditor.requestCompilationUnit(); | |
String packageName = cu.getOutputJavaPackage(); | |
String className = cu.getOutputJavaClassName(); | |
List problems = new ArrayList(); | |
if (packageName == null || className == null) | |
return problems; | |
Map mappingPositions = new HashMap(); | |
String javaSource = jetEditor.compilationHelper().getJavaCode(cu, mappingPositions); | |
JETJavaContentProblemRequestor problemRequestor = new JETJavaContentProblemRequestor(problems, mappingPositions, jetDocument); | |
try { | |
ICompilationUnit compilationUnit = null; | |
IJavaProject javaProject = jetEditor.getJavaProject(); | |
System.out.println("Project: " + javaProject.getElementName()); | |
if (!javaProject.getElementName().startsWith(".")) | |
System.out.println(); | |
IPackageFragmentRoot roots[] = javaProject.getPackageFragmentRoots(); | |
for (int i = 0; i < roots.length; i++) { | |
IPackageFragmentRoot root = roots[i]; | |
if (IPackageFragmentRoot.K_SOURCE != root.getKind()) { | |
continue; | |
} | |
IPackageFragment packageFragment = root.getPackageFragment(packageName); | |
if (packageFragment == null || !packageFragment.exists()) | |
packageFragment = root.createPackageFragment(packageName, true, new NullProgressMonitor()); | |
compilationUnit = packageFragment.createCompilationUnit((new StringBuilder(String.valueOf(className))).append(".java").toString(), javaSource, true, new NullProgressMonitor()); | |
break; | |
} | |
WorkingCopyOwner owner = new WorkingCopyOwner() { | |
}; | |
ICompilationUnit copy = compilationUnit.getWorkingCopy(owner, problemRequestor, null); | |
copy.reconcile(0, true, owner, null); | |
} catch (JavaModelException e) { | |
System.out.println(e); | |
Activator.log(e); | |
} | |
return problems; | |
} | |
/** | |
* @param javaProject | |
* @param cu | |
* @param codeSnippet | |
* @param offset | |
* @return | |
*/ | |
public static CompletionProposalCollector collect(IJavaProject javaProject, JETCompilationUnit cu, String codeSnippet, int offset) { | |
IEvaluationContext context; | |
CompletionProposalCollector proposalCollector; | |
context = javaProject.newEvaluationContext(); | |
List importList = new ArrayList(); | |
importList.addAll(cu.getImports()); | |
importList.add("org.eclipse.jet.JET2Context"); | |
importList.add("org.eclipse.jet.JET2Template"); | |
importList.add("org.eclipse.jet.JET2Writer"); | |
importList.add("org.eclipse.jet.taglib.RuntimeTagElement"); | |
importList.add("org.eclipse.jet.taglib.TagInfo"); | |
String imports[] = new String[importList.size()]; | |
importList.toArray(imports); | |
String packageName = cu.getOutputJavaPackage(); | |
if (packageName == null) { | |
Map options = CompileOptionsManager.getOptions(javaProject.getProject()); | |
if (options.isEmpty()) | |
options = JETCompilerOptions.getDefaultCompilerOptions(); | |
packageName = (String) options.get("org.eclipse.jet.compiledTemplatePackage"); | |
} | |
context.setPackageName(packageName); | |
context.setImports(imports); | |
proposalCollector = new CompletionProposalCollector(javaProject); | |
try { | |
context.codeComplete(codeSnippet, offset, proposalCollector); | |
return proposalCollector; | |
} catch (JavaModelException e) { | |
Activator.log(e); | |
} | |
return null; | |
} | |
/** | |
* @param editor | |
* @param viewer | |
* @param completionOffset | |
* @return | |
*/ | |
public static IJavaCompletionProposal[] getJavaCompletionProposal(JETTextEditor jetEditor, ITextViewer viewer, int completionOffset) { | |
IJavaProject javaProject; | |
String packageName; | |
String className; | |
Map mappingPositions; | |
IDocument javaDocument; | |
JETCompilationUnit cu = jetEditor.requestCompilationUnit(); | |
javaProject = jetEditor.getJavaProject(); | |
packageName = cu.getOutputJavaPackage(); | |
className = cu.getOutputJavaClassName(); | |
if (packageName == null) { | |
Map options = CompileOptionsManager.getOptions(javaProject.getProject()); | |
if (options.isEmpty()) | |
options = JETCompilerOptions.getDefaultCompilerOptions(); | |
packageName = (String) options.get("org.eclipse.jet.compiledTemplatePackage"); | |
} | |
if (className == null && (jetEditor.getEditorInput() instanceof IFileEditorInput)) { | |
IFile file = ((IFileEditorInput) jetEditor.getEditorInput()).getFile(); | |
String inputName = file.getFullPath().removeFileExtension().lastSegment(); | |
String baseClassName = makeJavaClassName(inputName); | |
className = baseClassName; | |
} | |
if (packageName == null || className == null) | |
return new IJavaCompletionProposal[0]; | |
mappingPositions = new HashMap(); | |
javaDocument = new Document(jetEditor.compilationHelper().getJavaCode(cu, mappingPositions)); | |
IJavaCompletionProposal results[]; | |
ICompilationUnit compilationUnit = null; | |
try { | |
IPackageFragmentRoot roots[] = javaProject.getPackageFragmentRoots(); | |
for (int i = 0; i < roots.length;) { | |
IPackageFragmentRoot root = roots[i]; | |
IPackageFragment packageFragment = root.getPackageFragment(packageName); | |
if (packageFragment == null || !packageFragment.exists()) | |
packageFragment = root.createPackageFragment(packageName, true, new NullProgressMonitor()); | |
compilationUnit = packageFragment.createCompilationUnit((new StringBuilder(String.valueOf(className))).append(".java").toString(), javaDocument.get(), true, new NullProgressMonitor()); | |
break; | |
} | |
CompletionProposalCollector proposalCollector = new CompletionProposalCollector(compilationUnit); | |
JETASTElement astElement = jetEditor.getASTElement(completionOffset); | |
Position javaPosition = (Position) mappingPositions.get(astElement); | |
int jetStart = getASTElementContentStart(astElement, viewer.getDocument()); | |
int javaStart = javaPosition.getOffset(); | |
int javaCompletionOffset = javaStart + (completionOffset - jetStart); | |
compilationUnit.codeComplete(javaCompletionOffset, proposalCollector); | |
Point selection = viewer.getSelectedRange(); | |
if (selection.y > 0) | |
proposalCollector.setReplacementLength(selection.y); | |
IJavaCompletionProposal javaProposals[] = proposalCollector.getJavaCompletionProposals(); | |
IJavaCompletionProposal keywordsProposals[] = proposalCollector.getKeywordCompletionProposals(); | |
IJavaCompletionProposal unsortedJavaProposals[] = new IJavaCompletionProposal[javaProposals.length + keywordsProposals.length]; | |
System.arraycopy(keywordsProposals, 0, unsortedJavaProposals, 0, keywordsProposals.length); | |
System.arraycopy(javaProposals, 0, unsortedJavaProposals, keywordsProposals.length, javaProposals.length); | |
List sortedJavaProposals = new LinkedList(); | |
for (int i = 0; i < unsortedJavaProposals.length; i++) { | |
IJavaCompletionProposal unsortedJavaProposal = unsortedJavaProposals[i]; | |
int index = 0; | |
for (Iterator iterator = sortedJavaProposals.iterator(); iterator.hasNext();) { | |
IJavaCompletionProposal proposal = (IJavaCompletionProposal) iterator.next(); | |
if (proposal.getRelevance() <= unsortedJavaProposal.getRelevance()) { | |
index = sortedJavaProposals.indexOf(proposal); | |
break; | |
} | |
} | |
sortedJavaProposals.add(index, unsortedJavaProposal); | |
} | |
results = new IJavaCompletionProposal[sortedJavaProposals.size()]; | |
sortedJavaProposals.toArray(results); | |
adjustResults(results, completionOffset); | |
return results; | |
} catch (JavaModelException e) { | |
Activator.log(e); | |
} | |
return new IJavaCompletionProposal[0]; | |
} | |
/** | |
* @param javaProject | |
* @param cu | |
* @param codeSnippet | |
* @param viewer | |
* @param offset | |
* @return | |
*/ | |
public static IJavaCompletionProposal[] getJavaCompletionProposal(IJavaProject javaProject, JETCompilationUnit cu, String codeSnippet, ITextViewer viewer, int offset) { | |
CompletionProposalCollector proposalCollector = collect(javaProject, cu, codeSnippet, offset); | |
if (proposalCollector != null) { | |
Point selection = viewer.getSelectedRange(); | |
if (selection.y > 0) | |
proposalCollector.setReplacementLength(selection.y); | |
IJavaCompletionProposal javaProposals[] = proposalCollector.getJavaCompletionProposals(); | |
IJavaCompletionProposal keywordsProposals[] = proposalCollector.getKeywordCompletionProposals(); | |
IJavaCompletionProposal unsortedJavaProposals[] = new IJavaCompletionProposal[javaProposals.length + keywordsProposals.length]; | |
System.arraycopy(keywordsProposals, 0, unsortedJavaProposals, 0, keywordsProposals.length); | |
System.arraycopy(javaProposals, 0, unsortedJavaProposals, keywordsProposals.length, javaProposals.length); | |
List sortedJavaProposals = new LinkedList(); | |
for (int i = 0; i < unsortedJavaProposals.length; i++) { | |
IJavaCompletionProposal unsortedJavaProposal = unsortedJavaProposals[i]; | |
int index = 0; | |
for (Iterator iterator = sortedJavaProposals.iterator(); iterator.hasNext();) { | |
IJavaCompletionProposal proposal = (IJavaCompletionProposal) iterator.next(); | |
if (proposal.getRelevance() <= unsortedJavaProposal.getRelevance()) { | |
index = sortedJavaProposals.indexOf(proposal); | |
break; | |
} | |
} | |
sortedJavaProposals.add(index, unsortedJavaProposal); | |
} | |
IJavaCompletionProposal results[] = new IJavaCompletionProposal[sortedJavaProposals.size()]; | |
sortedJavaProposals.toArray(results); | |
return results; | |
} else { | |
return new IJavaCompletionProposal[0]; | |
} | |
} | |
/** | |
* @param results | |
* @param offset | |
*/ | |
public static void adjustResults(IJavaCompletionProposal[] results, int offset) { | |
for (int i = 0; i < results.length; i++) | |
if (results[i] instanceof AbstractJavaCompletionProposal) { | |
AbstractJavaCompletionProposal proposal = (AbstractJavaCompletionProposal) results[i]; | |
proposal.setReplacementOffset(offset); | |
} | |
} | |
/** | |
* @param results | |
* @param offset | |
* @param allWords | |
*/ | |
public static void adjustJavaScriptletProposalResults(IJavaCompletionProposal[] results, int offset) { | |
if (results.length > 0) { | |
IJavaCompletionProposal iJavaCompletionProposal = results[0]; | |
if (iJavaCompletionProposal instanceof AbstractJavaCompletionProposal) { | |
AbstractJavaCompletionProposal proposal = (AbstractJavaCompletionProposal) iJavaCompletionProposal; | |
int replacementLength = proposal.getReplacementLength(); | |
adjustResults(results, offset - replacementLength); | |
} | |
} | |
} | |
/** | |
* @param name | |
* @return | |
*/ | |
public static String makeJavaClassName(String name) { | |
StringBuffer result = new StringBuffer("_jet_"); | |
for (int i = 0; i < name.length(); i++) { | |
char c = name.charAt(i); | |
if (Character.isJavaIdentifierPart(c)) | |
result.append(c); | |
} | |
return result.toString(); | |
} | |
} |