/******************************************************************************* * Copyright (c) 2007-2012 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.common.el.core.resolver; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.ITextViewer; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr; import org.jboss.tools.common.el.core.ELCorePlugin; import org.jboss.tools.common.el.core.model.ELExpression; import org.jboss.tools.common.el.core.parser.ELParserFactory; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * This class helps to find var/value attributes in DOM tree. * @author Alexey Kazakov */ @SuppressWarnings("restriction") public class ElVarSearcher { private final static String VAR_ATTRIBUTE_NAME = "var"; //$NON-NLS-1$ private final static String VALUE_ATTRIBUTE_NAME = "value"; //$NON-NLS-1$ private IFile file; private ELCompletionEngine engine; /** * Constructor. * @param project Seam project where we will look for vars. * @param file File where we will look for vars. * @param engine Competion Engine that we will use for resolving vars. */ public ElVarSearcher(IFile file, ELCompletionEngine engine) { this.file = file; this.engine = engine; } /** * Constructor. * @param project Seam project where we will look for vars. * @param engine Competion Engine that we will use for resolving vars. */ public ElVarSearcher(ELCompletionEngine engine) { this(null, engine); } /** * @param file File where we will look for vars. */ public void setFile(IFile file) { this.file = file; } /** * @param viewer * @param offset * @return */ public static Node getNode(ITextViewer viewer, int offset) { IndexedRegion treeNode = ContentAssistUtils.getNodeAt(viewer, offset); if(treeNode instanceof Node) { return (Node)treeNode; } return null; } /** * @param viewer * @param offset * @return */ public static Node getNode(IFile file, int offset) { IndexedRegion treeNode = getNodeAt(file, offset); if(treeNode instanceof Node) { return (Node)treeNode; } return null; } /** * Returns the closest IndexedRegion for the offset and viewer allowing * for differences between viewer offsets and model positions. note: this * method returns an IndexedRegion for read only * * @param file * the file whose document is used to compute the proposals * @param documentOffset * an offset within the document for which completions should * be computed * @return an IndexedRegion */ public static IndexedRegion getNodeAt(IFile file, int documentOffset) { IndexedRegion node = null; if (file != null) { IModelManager mm = StructuredModelManager.getModelManager(); IStructuredModel model = null; if (mm != null) { try { model = mm.getModelForRead(file); if (model != null) { int lastOffset = documentOffset; node = model.getIndexedRegion(documentOffset); while (node == null && lastOffset >= 0) { lastOffset--; node = model.getIndexedRegion(lastOffset); } } } catch (IOException e) { ELCorePlugin.getDefault().logError(e); node = null; } catch (CoreException e) { ELCorePlugin.getDefault().logError(e); node = null; } finally { if (model != null) model.releaseFromRead(); } } } return node; } /** * @param node * @return All var/value that can be used in this position and null if can't find anyone. */ public List<Var> findAllVars(ITextViewer viewer, int offset) { Node node = getNode(viewer, offset); if(node!=null) { return findAllVars(node); } return Collections.emptyList(); } /** * @param node * @return All var/value that can be used in this position and null if can't find anyone. */ public List<Var> findAllVars(IFile file, int offset) { return findAllVars(file, offset, engine.getParserFactory()); } /** * @param context * @param offset * @param resolver * @return All var/value that can be used in this position and null if can't find anyone. */ public static List<Var> findAllVars(ELContext context, int offset, ELResolver resolver) { Node node = getNode(context.getResource(), offset); if(node!=null) { List<Var> result = findAllVars(node, resolver.getParserFactory()); for (Var v: result) v.setFile(context.getResource()); return result; } return Collections.emptyList(); } /** * @param node * @param factory * @return All var/value that can be used in this position and null if can't find anyone. */ public static List<Var> findAllVars(IFile file, int offset, ELParserFactory factory) { Node node = getNode(file, offset); if(node!=null) { List<Var> result = findAllVars(node, factory); for (Var v: result) v.setFile(file); return result; } return Collections.emptyList(); } /** * @param node * @return All var/value that can be used in this position and null if can't find anyone. */ public static List<Var> findAllVars(ITextViewer viewer, int offset, ELParserFactory factory) { Node node = getNode(viewer, offset); if(node!=null) { return findAllVars(node, factory); } return Collections.emptyList(); } /** * @param node * @return All var/value that can be used in node and null if can't find anyone. */ public List<Var> findAllVars(Node node) { return findAllVars(node, engine.getParserFactory()); } /** * @param node * @param factory * @return All var/value that can be used in node and null if can't find anyone. */ public static List<Var> findAllVars(Node node, ELParserFactory factory) { ArrayList<Var> vars = new ArrayList<Var>(); Node parentNode = node; while(parentNode!=null) { Var var = findVar(parentNode, factory); if(var!=null) { vars.add(0, var); } parentNode = parentNode.getParentNode(); } return vars; } /** * @param node * @return found var/value that can be used in this position and null if can't find anyone. */ public Var findVar(IFile file, int offset) { Node node = getNode(file, offset); if(node!=null) { Var result = findVar(node); if(result != null) result.setFile(file); return result; } return null; } /** * Finds var/value attribute in node * @param node * @param vars * @return found var/value or null */ public Var findVar(Node node) { return findVar(node, engine.getParserFactory()); } /** * Finds var/value attribute in node * @param node * @param vars * @param factory * @return found var/value or null */ public static Var findVar(Node node, ELParserFactory factory) { if(factory!=null && node!=null && Node.ELEMENT_NODE == node.getNodeType()) { Element element = (Element)node; synchronized (element) { if(element.hasAttribute(VAR_ATTRIBUTE_NAME)) { String var = element.getAttribute(VAR_ATTRIBUTE_NAME); int declOffset = 0; int declLength = 0; Node varAttr = element.getAttributeNode(VAR_ATTRIBUTE_NAME); if (varAttr instanceof IDOMAttr) { int varNameStart = ((IDOMAttr)varAttr).getNameRegionStartOffset(); int varNameEnd = ((IDOMAttr)varAttr).getNameRegionEndOffset(); declOffset = varNameStart; declLength = varNameEnd - varNameStart; } var = var.trim(); if(!"".equals(var)) { //$NON-NLS-1$ if(element.hasAttribute(VALUE_ATTRIBUTE_NAME)) { String value = element.getAttribute(VALUE_ATTRIBUTE_NAME); value = value.trim(); Var newVar = new Var(factory, var, value, declOffset, declLength); if(newVar.getElToken()!=null) { return newVar; } } } } } } return null; } /** * Finds var in list of vars that is used in given EL. * @param el EL without brackets. * @param vars * @param initializeNestedVars * @return */ public Var findVarForEl(String el, ELContext context, List<Var> vars, boolean initializeNestedVars) { if(vars!=null) { ArrayList<Var> parentVars = new ArrayList<Var>(); for (Var var : vars) { if(isRelevantVar(var, el, context, vars, parentVars, initializeNestedVars)) { return var; } } } return null; } public List<Var> findVarsForEl(String el, ELContext context, List<Var> vars, boolean initializeNestedVars) { List<Var> result = new ArrayList<Var>(); ArrayList<Var> parentVars = new ArrayList<Var>(); for (Var var : vars) { if(isRelevantVar(var, el, context, vars, parentVars, initializeNestedVars)) { result.add(var); } } return result; } private boolean isRelevantVar(Var var, String el, ELContext context, List<Var> vars, ArrayList<Var> parentVars, boolean initializeNestedVars) { boolean result = false; ELExpression token = var.getElToken(); if(token!=null && !token.getText().endsWith(".")) { //$NON-NLS-1$ String varName = var.getName(); if(el.equals(varName) || el.startsWith(varName.trim()+".")) { //$NON-NLS-1$ if(var.getElToken()!=null && initializeNestedVars) { ELContext c = context; if(!var.getFile().equals(context.getResource())) { //TODO } Var parentVar = findVarForEl(var.getElToken().getText(), c, parentVars, true); if(parentVar!=null) { ELExpression resolvedToken = parentVar.getResolvedElToken(); if(resolvedToken==null && parentVar.getElToken()!=null) { try { // Initialize parent vars. engine.resolveELOperand(file, context, var.getElToken(), true, parentVars, this, (var.getRegion() == null ? 0 : var.getRegion().getOffset())); resolvedToken = parentVar.getResolvedElToken(); } catch (StringIndexOutOfBoundsException e) { ELCorePlugin.getPluginLog().logError(e); } catch (BadLocationException e) { ELCorePlugin.getPluginLog().logError(e); } } if(resolvedToken!=null) { String oldText = var.getElToken().getText(); String newValue = "#{" + resolvedToken.getText() + oldText.substring(parentVar.getName().length()) + "}"; //$NON-NLS-1$ //$NON-NLS-2$ var.value = newValue; var.elToken = var.parseEl(newValue); } } } result = true; } } parentVars.add(var); return result; } }