/******************************************************************************* * Copyright (c) 2005, 2011 BEA Systems 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: * BEA Systems - initial implementation * * Bug 154474 EL: 'and', 'or', ... operator * https://bugs.eclipse.org/bugs/show_bug.cgi?id=154474 * Bernhard Huemer <bernhard.huemer@gmail.com> * *******************************************************************************/ package org.eclipse.jst.jsp.core.internal.java.jspel; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jface.text.Position; import org.eclipse.jst.jsp.core.internal.JSPCoreMessages; import org.eclipse.jst.jsp.core.internal.JSPCorePlugin; import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.CMDocumentImpl; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TLDCMDocumentManager; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.TaglibTracker; import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDFunction; import org.eclipse.jst.jsp.core.internal.preferences.JSPCorePreferenceNames; import org.eclipse.jst.jsp.core.jspel.ELProblem; import org.eclipse.osgi.util.NLS; import org.eclipse.wst.sse.core.internal.FileBufferModelManager; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection; import org.eclipse.wst.sse.core.internal.validate.ValidationMessage; import org.eclipse.wst.validation.internal.provisional.core.IMessage; public class ELGeneratorVisitor implements JSPELParserVisitor { private static final String PREFERENCE_NODE_QUALIFIER = JSPCorePlugin.getDefault().getBundle().getSymbolicName(); private static final String ENDL = "\n"; //$NON-NLS-1$ private static final String fExpressionHeader1 = "public String _elExpression"; //$NON-NLS-1$ private static final String fExpressionHeader2 = "()" + ENDL + //$NON-NLS-1$ "\t\tthrows java.io.IOException, javax.servlet.ServletException, javax.servlet.jsp.JspException {" + ENDL + //$NON-NLS-1$ "javax.servlet.jsp.PageContext pageContext = null;" + ENDL + //$NON-NLS-1$ "java.util.Map param = null;" + ENDL + //$NON-NLS-1$ "java.util.Map paramValues = null;" + ENDL + //$NON-NLS-1$ "java.util.Map header = null;" + ENDL + //$NON-NLS-1$ "java.util.Map headerValues = null;" + ENDL + //$NON-NLS-1$ "java.util.Map cookie = null;" + ENDL + //$NON-NLS-1$ "java.util.Map initParam = null;" + ENDL + //$NON-NLS-1$ "java.util.Map pageScope = null;" + ENDL + //$NON-NLS-1$ "java.util.Map requestScope = null;" + ENDL + //$NON-NLS-1$ "java.util.Map sessionScope = null;" + ENDL + //$NON-NLS-1$ "java.util.Map applicationScope = null;" + ENDL + //$NON-NLS-1$ "return \"\"+( "; //$NON-NLS-1$ private static final String fExpressionHeader2_param = "()" + ENDL + //$NON-NLS-1$ "\t\tthrows java.io.IOException, javax.servlet.ServletException, javax.servlet.jsp.JspException {" + ENDL + //$NON-NLS-1$ "javax.servlet.jsp.PageContext pageContext = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, String> param = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, String[]> paramValues = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, String> header = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, String[]> headerValues = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, javax.servlet.http.Cookie> cookie = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, String> initParam = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, Object> pageScope = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, Object> requestScope = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, Object> sessionScope = null;" + ENDL + //$NON-NLS-1$ "java.util.Map<String, Object> applicationScope = null;" + ENDL + //$NON-NLS-1$ "return \"\"+( "; //$NON-NLS-1$ private static final String fJspImplicitObjects[] = { "pageContext" }; //$NON-NLS-1$ private static final String fJspImplicitMaps[] = { "param", "paramValues", "header", "headerValues", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ "cookie", "initParam", "pageScope", "requestScope", "sessionScope", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ "applicationScope" }; //$NON-NLS-1$ private static final String fHttpServletRequestBooleanMethodNames[] = {"requestedSessionIdFromCookie", "requestedSessionIdFromUrl", "requestedSessionIdFromURL", "requestedSessionIdValid", "userInRole", "secure"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ private static final HashMap fJSPImplicitObjectMap = new HashMap(fJspImplicitObjects.length); private static final Set fHttpServletRequestBooleanMethods = new HashSet(fHttpServletRequestBooleanMethodNames.length); static { for(int i = 0; i < fJspImplicitObjects.length; i++) { fJSPImplicitObjectMap.put(fJspImplicitObjects[i], new Boolean(true)); } for(int i = 0; i < fJspImplicitMaps.length; i++) { fJSPImplicitObjectMap.put(fJspImplicitMaps[i], new Boolean(false)); } for (int i = 0; i < fHttpServletRequestBooleanMethodNames.length; i++) { fHttpServletRequestBooleanMethods.add(fHttpServletRequestBooleanMethodNames[i]); } } private static final byte[] HTTP_REQUEST = new byte[0]; private static final String fFooter = " );" + ENDL + "}" + ENDL; //$NON-NLS-1$ //$NON-NLS-2$ private StringBuffer fResult; private Map fCodeMap; private int fOffsetInUserCode; private static int methodCounter = 0; private IStructuredDocument fDocument = null; private int fContentStart; private static Map fOperatorMap; // start of the generated function definition, if any: private int fGeneratedFunctionStart; // this flag lets us know if we were unable to generate for some reason. One possible reason is that the expression // contains a reference to a variable for which information is only available at runtime. private boolean fCanGenerate = true; private IStructuredDocumentRegion fCurrentNode; private boolean fUseParameterizedTypes; private List fELProblems; private IScopeContext[] fScopeContexts = null; /** * Tranlsation of XML-style operators to java */ static { fOperatorMap = new HashMap(); fOperatorMap.put("gt", ">"); //$NON-NLS-1$ //$NON-NLS-2$ fOperatorMap.put("lt", "<"); //$NON-NLS-1$ //$NON-NLS-2$ fOperatorMap.put("ge", ">="); //$NON-NLS-1$ //$NON-NLS-2$ fOperatorMap.put("le", "<="); //$NON-NLS-1$ //$NON-NLS-2$ fOperatorMap.put("mod", "%"); //$NON-NLS-1$ //$NON-NLS-2$ fOperatorMap.put("eq", "=="); //$NON-NLS-1$ //$NON-NLS-2$ fOperatorMap.put("and", "&&"); //$NON-NLS-1$ //$NON-NLS-2$ fOperatorMap.put("or", "||"); //$NON-NLS-1$ //$NON-NLS-2$ fOperatorMap.put("not", "!"); //$NON-NLS-1$ //$NON-NLS-2$ } /** * The constructor squirrels away a few things we'll need later * * @param result * @param codeMap * @param translator * @param jspReferenceRegion * @param contentStart */ public ELGeneratorVisitor(StringBuffer result, IStructuredDocumentRegion currentNode, Map codeMap, IStructuredDocument document, ITextRegionCollection jspReferenceRegion, int contentStart) { fResult = result; fCodeMap = codeMap; fOffsetInUserCode = result.length(); fContentStart = contentStart; fDocument = document; fCurrentNode = currentNode; fGeneratedFunctionStart = -1; //set when generating function definition fUseParameterizedTypes = compilerSupportsParameterizedTypes(); fELProblems = new ArrayList(); fScopeContexts = getScopeContexts(); } /** * Append a token to the output stream. Automatically calculating mapping. * * @param token */ private void append(Token token) { append(token.image, token.beginColumn - 1, token.endColumn); } /** * Append a translation for the corresponding input token. * * @param translated * @param token */ private void append(String translated, Token token) { append(translated, token.beginColumn - 1, token.endColumn); } /** * Append a string explicitly giving the input mapping. * * @param newText * @param jspPositionStart * @param jspPositionEnd */ private void append(String newText, int jspPositionStart, int jspPositionEnd) { fResult.append(newText); Position javaRange = new Position(fOffsetInUserCode, newText.length()); Position jspRange = new Position(fContentStart + jspPositionStart, jspPositionEnd - jspPositionStart); fCodeMap.put(javaRange, jspRange); fOffsetInUserCode += newText.length(); } /** * Append text that will be unmapped and therefore will not be available for completion. * * @param newText */ private void append(String newText) { fResult.append(newText); fOffsetInUserCode += newText.length(); } /** * Generate a function invocation. * * @param fullFunctionName * @return */ protected String genFunction(String fullFunctionName) { TLDCMDocumentManager docMgr = TaglibController.getTLDCMDocumentManager(fDocument); int colonIndex = fullFunctionName.indexOf(':'); String prefix = fullFunctionName.substring(0, colonIndex); String functionName = fullFunctionName.substring(colonIndex + 1); if (docMgr == null) return null; Iterator taglibs = docMgr.getCMDocumentTrackers(fCurrentNode.getStartOffset()).iterator(); while (taglibs.hasNext()) { TaglibTracker tracker = (TaglibTracker)taglibs.next(); if(tracker.getPrefix().equals(prefix)) { CMDocumentImpl doc = (CMDocumentImpl)tracker.getDocument(); List functions = doc.getFunctions(); for(Iterator it = functions.iterator(); it.hasNext(); ) { TLDFunction function = (TLDFunction)it.next(); if(function.getName().equals(functionName)) { String javaFuncName = getFunctionNameFromSignature(function.getSignature()); if (javaFuncName == null) javaFuncName = functionName; return function.getClassName() + "." + javaFuncName; //$NON-NLS-1$ } } } } return null; } /** * Handle a simple node -- fallback */ public Object visit(SimpleNode node, Object data) { return(node.childrenAccept(this, data)); } static synchronized int getMethodCounter() { return methodCounter++; } /** * Handle top-level expression */ public Object visit(ASTExpression node, Object data) { return node.childrenAccept(this, data); } public void startFunctionDefinition(int start) { fGeneratedFunctionStart = fResult.length(); append(fExpressionHeader1, start, start); append(Integer.toString(getMethodCounter()), start, start); if (fUseParameterizedTypes) append(fExpressionHeader2_param, start, start); else append(fExpressionHeader2, start, start); } public void endFunctionDefinition(int end) { if (fGeneratedFunctionStart < 0) { throw new IllegalStateException("Cannot end function definition because none has been started."); //$NON-NLS-1$ } append(fFooter, end, end); // something is preventing good code generation so empty out the result // and the map. if (!fCanGenerate) { fResult.delete(fGeneratedFunctionStart, fResult.length()); fOffsetInUserCode = fResult.length(); // remove all fCodeMap entries for the removed code: for (Iterator it = fCodeMap.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Entry) it.next(); if (entry.getKey() instanceof Position) { Position pos = (Position) entry.getKey(); if (pos.getOffset() >= fGeneratedFunctionStart) { it.remove(); } } } } fGeneratedFunctionStart = -1; } private boolean compilerSupportsParameterizedTypes() { if (fDocument != null) { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IPath location = TaglibController.getLocation(fDocument); if (location != null && location.segmentCount() > 0) { IJavaProject project = JavaCore.create(root.getProject(location.segment(0))); String compliance = project.getOption(JavaCore.COMPILER_SOURCE, true); try { return Float.parseFloat(compliance) >= 1.5; } catch (NumberFormatException e) { return false; } } } return false; } /** * Generically generate an operator node. * * @param node * @param data */ private void generateOperatorNode(ASTOperatorExpression node, Object data) { for(int i = 0; i < node.children.length; i++) { node.children[i].jjtAccept(this, data); if( node.children.length - i > 1) { appendOperator((Token)node.getOperatorTokens().get(i)); } } } /** * Append an operator to the output stream after translation (if any) * * @param token * @return */ private String appendOperator(Token token) { String tokenImage = token.image.trim(); String translated = (String)fOperatorMap.get(tokenImage); if(null != translated) { append(translated, token); } else { append(token); } return(translated); } /** * Handle or Expression */ public Object visit(ASTOrExpression node, Object data) { generateOperatorNode(node, data); return(null); } /** * Handle and expression */ public Object visit(ASTAndExpression node, Object data) { generateOperatorNode(node, data); return(null); } /** * Handle equality */ public Object visit(ASTEqualityExpression node, Object data) { generateOperatorNode(node, data); return(null); } /** * Handle Relational */ public Object visit(ASTRelationalExpression node, Object data) { generateOperatorNode(node, data); return(null); } /** * Handle addition */ public Object visit(ASTAddExpression node, Object data) { generateOperatorNode(node, data); return(null); } /** * Handle multiply */ public Object visit(ASTMultiplyExpression node, Object data) { generateOperatorNode(node, data); return(null); } /** * Choice Expression (ternary operator) */ public Object visit(ASTChoiceExpression node, Object data) { node.children[0].jjtAccept(this, data); append("?"); //$NON-NLS-1$ node.children[1].jjtAccept(this, data); append(":"); //$NON-NLS-1$ node.children[2].jjtAccept(this,data); return null; } /** * Handle unary */ public Object visit(ASTUnaryExpression node, Object data) { if(JSPELParserConstants.EMPTY == node.firstToken.kind) { append("((null == "); //$NON-NLS-1$ node.childrenAccept(this, data); append(") || ("); //$NON-NLS-1$ node.childrenAccept(this, data); append(").isEmpty())"); //$NON-NLS-1$ } else if(JSPELParserConstants.NOT1 == node.firstToken.kind || JSPELParserConstants.NOT2 == node.firstToken.kind) { append("(!"); //$NON-NLS-1$ node.childrenAccept(this, data); append(")"); //$NON-NLS-1$ } else if(JSPELParserConstants.MINUS == node.firstToken.kind) { append("(-"); //$NON-NLS-1$ node.childrenAccept(this, data); append(")"); //$NON-NLS-1$ } else { node.childrenAccept(this, data); } return null; } /** * Value node */ public Object visit(ASTValue node, Object data) { if(node.jjtGetNumChildren() >= 2) { if(node.jjtGetChild(0) instanceof ASTValuePrefix && node.jjtGetChild(1) instanceof ASTValueSuffix) { ASTValuePrefix prefix = (ASTValuePrefix) node.jjtGetChild(0); ASTValueSuffix suffix = (ASTValueSuffix) node.jjtGetChild(1); //content assist can cause a null pointer here without the extra null check if(prefix.firstToken.image.equals("pageContext") && suffix.getPropertyNameToken() != null && suffix.getPropertyNameToken().image.equals("request")) { append("((HttpServletRequest)"); data = HTTP_REQUEST; } } } return node.childrenAccept(this, data); } /** * Value Prefix */ public Object visit(ASTValuePrefix node, Object data) { // this is a raw identifier. May sure it's an implicit object. // This is the primary place where modification is needed to // support JSF backing beans. if(null == node.children) { if(isCompletingObject(node.firstToken.image)) { append(node.firstToken); } else { fCanGenerate = false; } return(null); } return node.childrenAccept(this, data); } /** * Function for testing implicit objects. * * @param image * @return */ private boolean isCompletingObject(String image) { Boolean value = (Boolean)fJSPImplicitObjectMap.get(image); return null == value ? false : value.booleanValue(); } /** * Value suffix */ public Object visit(ASTValueSuffix node, Object data) { if(JSPELParserConstants.LBRACKET == node.firstToken.kind) { fCanGenerate = false; } else if(null != node.getPropertyNameToken()) { Token suffix = node.getPropertyNameToken(); String ucaseName = suffix.image.substring(0, 1).toUpperCase() + suffix.image.substring(1, suffix.image.length()); // This is a special case. Note that the type system, no matter how much type information // we would have wouldn't give us the correct result. We're looking for "pageContext.request" // here and will add a downcast to (HTTPServletRequest) String prefix = "get"; //$NON-NLS-1$ append(node.firstToken); if (data == HTTP_REQUEST && !"request".equals(suffix.image)) { //$NON-NLS-1$ if (fHttpServletRequestBooleanMethods.contains(suffix.image)) { prefix = "is"; //$NON-NLS-1$ } } append(prefix + ucaseName + "()", suffix); //$NON-NLS-1$ SimpleNode parent = (SimpleNode) node.jjtGetParent(); if(suffix.image.equals("request") && parent instanceof ASTValue && //$NON-NLS-1$ parent.jjtGetParent() instanceof ASTUnaryExpression && parent.firstToken.image.equals("pageContext")) { //$NON-NLS-1$ append(")"); } } else if(node.getLastToken().image.equals(".") && node.getLastToken().next.image.equals("")) { //$NON-NLS-1$ //$NON-NLS-2$ //this allows for content assist in the case of something along the lines of "pageContext." and then ctl-space append(node.firstToken); append("get()", node.getLastToken().beginColumn, node.getLastToken().beginColumn); //$NON-NLS-1$ } else { append(node.firstToken); } return null; } /** * Function invocation */ public Object visit(ASTFunctionInvocation node, Object data) { String functionTranslation = genFunction(node.getFullFunctionName()); if(null != functionTranslation) { //find the token representing the function name Token jspFuncNameToken = getJSPFuncNameToken(node); /* if there is a dot in the function name then separate out the class path * from the function name and append. * else just append * in both cases use the jsp function name token as the mapped token */ int indexOfDot = functionTranslation.lastIndexOf('.'); if(indexOfDot != -1) { String funcClass = functionTranslation.substring(0,indexOfDot+1); String funcName = functionTranslation.substring(indexOfDot+1); append(funcClass, jspFuncNameToken); append(funcName, jspFuncNameToken); } else { append(functionTranslation, jspFuncNameToken); } //append any parameters append("("); if(node.children != null) { for(int i = 0; i < node.children.length; i++) { node.children[i].jjtAccept(this, data); if( node.children.length - i > 1){ append(","); //$NON-NLS-1$ } } } append(")"); //$NON-NLS-1$ } else { final int sev = getProblemSeverity(JSPCorePreferenceNames.VALIDATION_EL_FUNCTION_UNDEFINED); if (sev != ValidationMessage.IGNORE) { //column offsets are 1 based not 0 based, thus subtract one final int problemOffset = fContentStart + node.getFirstToken().beginColumn - 1; final int problemLength = node.getLastToken().endColumn - 1; //could not find function translation so report error fELProblems.add(new ELProblem(sev, new Position(problemOffset, problemLength), NLS.bind(JSPCoreMessages.JSPELTranslator_0, node.getFullFunctionName()))); } //error message to be injected into translation purely for debugging purposes String errorMsg = "\"Could not find function translation for: " + node.getFullFunctionName() + "\""; //$NON-NLS-1$ //$NON-NLS-2$ append(errorMsg); } return null; } /** * @return the {@link ELProblem}s found by this visitor */ public List getELProblems() { return fELProblems; } /** * Literal */ public Object visit(ASTLiteral node, Object data) { if (isSingleQuotedStringLiteral(node)) { //replace the single quotes with double quotes quotes //so java compiler will be happy //(see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=104943) String image = node.firstToken.image; image = "\"" + image.substring(1, image.length()-1) + "\""; //$NON-NLS-1$ //$NON-NLS-2$ node.firstToken.image = image; } append(node.firstToken); return null; } /** * Indicates whether the given ASTLiteral is a single quoted string literal, * As opposed to a double quoted ASTLiteral * * @param node the ASTLiteral to check to see if it is single quoted * * @return true, if the given token is a single quoted string literal, * false otherwise */ private static boolean isSingleQuotedStringLiteral(ASTLiteral node) { String content = node.firstToken.image; return content.length() > 1 && content.startsWith("'") && content.endsWith("'"); //$NON-NLS-1$ // $NON-NLS-2$ } /** * <p>Given a method signature parse out the method name and return it. * The method name in the signature is found by finding a word with * whitespace before it and a '<code>(</code>' after it.</p> * * @param methodSignature the signature of the method to get the method name out of. * @return the method name from the given signature, or <code>null</code> if it * can not be found. */ private static String getFunctionNameFromSignature (String methodSignature) { int length = methodSignature.length(); char c = 0; int identifierStart = -1; int whitespaceStart = -1; // keep track of the index of the last identifier before the ( for (int i = 0; i < length; i++) { c = methodSignature.charAt(i); if (Character.isJavaIdentifierPart(c) && whitespaceStart >= identifierStart) identifierStart = i; else if (Character.isWhitespace(c)) whitespaceStart = i; else if (c == '(') { if (identifierStart >= 0) { return methodSignature.substring(identifierStart, i).trim(); } } } return null; } /** * Returns the {@link Token} the represents the function name in * the {@link ASTFunctionInvocation}. This is designated as the * first token after the {@link Token} whose image is ":". * If such a token can not be found then the first token of the * {@link ASTFunctionInvocation} is returned. * * @param funcInvo the {@link ASTFunctionInvocation} to find the function name {@link Token} in * @return the {@link Token} in the given {@link ASTFunctionInvocation} that represents the * function name, or if that can't be found the first {@link Token} in the {@link ASTFunctionInvocation}. */ private Token getJSPFuncNameToken(ASTFunctionInvocation funcInvo) { Token funcNameToken = funcInvo.getFirstToken(); Token temp = funcInvo.getFirstToken(); do { if(temp.image.equals(":")) { funcNameToken = temp.next; } } while(temp.next != null && funcNameToken == null); return funcNameToken; } /** * @param key preference key used to get the severity for a problem * @param contexts preference service contexts * @return The severity of the problem represented by the given preference key */ private int getProblemSeverity(String key) { return Platform.getPreferencesService().getInt(PREFERENCE_NODE_QUALIFIER, key, IMessage.NORMAL_SEVERITY, fScopeContexts); } private IScopeContext[] getScopeContexts() { IScopeContext[] scopes = new IScopeContext[]{new InstanceScope(), new DefaultScope()}; final IFile file = getFile(); if (file != null && file.exists()) { final IProject project = file.getProject(); if (project.exists()) { final ProjectScope projectScope = new ProjectScope(project); if (projectScope.getNode(PREFERENCE_NODE_QUALIFIER).getBoolean(JSPCorePreferenceNames.VALIDATION_USE_PROJECT_SETTINGS, false)) { scopes = new IScopeContext[]{projectScope, new InstanceScope(), new DefaultScope()}; } } } return scopes; } private IFile getFile() { IFile f = null; if (fDocument != null) { final ITextFileBuffer buffer = FileBufferModelManager.getInstance().getBuffer(fDocument); if (buffer != null) { final IPath path = buffer.getLocation(); if (path.segmentCount() > 1) { f = ResourcesPlugin.getWorkspace().getRoot().getFile(path); } if (f != null && f.isAccessible()) { return f; } } } return null; } }