/* * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.coldFusion.model.psi; import com.intellij.coldFusion.model.CfmlLanguage; import com.intellij.coldFusion.model.files.CfmlFile; import com.intellij.coldFusion.model.files.CfmlFileType; import com.intellij.coldFusion.model.lexer.CfscriptTokenTypes; import com.intellij.coldFusion.model.psi.impl.CfmlAttributeImpl; import com.intellij.coldFusion.model.psi.impl.CfmlAttributeNameImpl; import com.intellij.coldFusion.model.psi.impl.CfmlNamedAttributeImpl; import com.intellij.coldFusion.model.psi.impl.CfmlTagScriptImpl; import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * Created by Lera Nikolaenko * Date: 12.02.2009 */ public class CfmlPsiUtil { @Nullable public static Collection<String> findBetween(@NotNull String source, @NotNull String startMarker, @NotNull String endMarker) { int fromIndex = 0; Collection<String> collection = new LinkedList<>(); while (fromIndex < source.length() && fromIndex >= 0) { int start = source.indexOf(startMarker, fromIndex); if (start < 0) { break; } start += startMarker.length(); final int end = source.indexOf(endMarker, start); if (end < start) { break; } collection.add(source.substring(start, end)); fromIndex = end + endMarker.length(); } return collection; } @Nullable public static TextRange findRange(@NotNull String source, @NotNull String startMarker, @NotNull String endMarker) { int start = source.indexOf(startMarker); if (start < 0) { return null; } start += startMarker.length(); final int end = source.indexOf(endMarker, start); if (end < start) { return null; } return new TextRange(start, end); } private final static Set<String> OUR_TRANSPARENT_FUNCTIONS = new HashSet<>(); static { OUR_TRANSPARENT_FUNCTIONS.add("cfsilent"); OUR_TRANSPARENT_FUNCTIONS.add(CfmlTagScriptImpl.TAG_NAME); OUR_TRANSPARENT_FUNCTIONS.add("cfprocessingdirective"); OUR_TRANSPARENT_FUNCTIONS.add("cfsavecontent"); OUR_TRANSPARENT_FUNCTIONS.add("cflock"); } public static boolean processDeclarations(@NotNull final PsiScopeProcessor processor, @NotNull final ResolveState state, @Nullable final PsiElement lastParent, @NotNull final PsiElement currentElement) { PsiElement element = (lastParent == null ? currentElement.getLastChild() : lastParent.getPrevSibling()); do { if (element instanceof PsiNamedElement && !(element instanceof CfmlFunction)) { // functions are processed separately if (!processor.execute(element, state)) { return false; } } else if (element instanceof CfmlTag) { if (!(element instanceof CfmlFunction)) { // functions are processed separately final PsiElement psiElement = ((CfmlTag)element).getDeclarativeElement(); if (psiElement != null && !processor.execute(psiElement, state)) { return false; } if (OUR_TRANSPARENT_FUNCTIONS.contains(((CfmlTag)element).getTagName())) { if (!processDeclarations(processor, state, null, element)) { return false; } } } } else if (element instanceof CfmlAssignmentExpression) { final CfmlAssignmentExpression assignmentExpression = (CfmlAssignmentExpression)element; CfmlVariable assignedVariable = assignmentExpression.getAssignedVariable(); if (assignedVariable != null && lastParent != assignmentExpression.getRightHandExpr() && !processor.execute(assignedVariable, state)) { return false; } } if (element == null) { return true; } element = element.getPrevSibling(); } while (element != null); return true; } @Nullable public static CfmlTypedElement getTypedQualifierInner(PsiElement element) { if (element == null) { return null; } PsiElement child = element.getFirstChild(); while (child != null) { if (child instanceof CfmlTypedElement) { return (CfmlTypedElement)child; } child = child.getNextSibling(); } return null; } @Nullable public static CfmlReference getQualifierInner(PsiElement element) { if (element == null) { return null; } PsiElement child = element.getFirstChild(); while (child != null) { if (child instanceof CfmlReferenceExpression) { return (CfmlReferenceExpression)child; } if (child instanceof CfmlFunctionCallExpression) { return ((CfmlFunctionCallExpression)child).getReferenceExpression(); } child = child.getNextSibling(); } return null; } @Nullable public static PsiType getTypeByName(String typeName, Project project) { return JavaPsiFacade.getInstance(project).getElementFactory().createTypeByFQClassName(typeName, GlobalSearchScope.allScope(project)); } public static CfmlFile createDummyFile(Project project, String text) { final String fileName = "dummy." + CfmlFileType.INSTANCE.getDefaultExtension(); return (CfmlFile)PsiFileFactory.getInstance(project).createFileFromText(fileName, CfmlLanguage.INSTANCE, text); } @NotNull public static CfmlReferenceExpression createReferenceExpression(final String text, final Project project) { final CfmlFile dummyFile = createDummyFile(project, "<cfset " + text + " = 0>"); final PsiElement tag = dummyFile.getFirstChild(); assert tag != null; final CfmlAssignmentExpression assignment = PsiTreeUtil.getChildOfType(tag, CfmlAssignmentExpression.class); assert assignment != null; final CfmlReferenceExpression expression = PsiTreeUtil.getChildOfType(assignment, CfmlReferenceExpression.class); assert expression != null; return expression; } @NotNull public static PsiElement createIdentifier(final String text, final Project project) { final CfmlReferenceExpression reference = createReferenceExpression(text, project); final PsiElement identifier = reference.getFirstChild(); assert identifier != null; final ASTNode identifierNode = identifier.getNode(); assert identifierNode != null; assert identifierNode.getElementType() == CfscriptTokenTypes.IDENTIFIER; return identifier; } @NotNull public static PsiElement createConstantString(final String text, final Project project) { final CfmlFile dummyFile = createDummyFile(project, "<cffunction name=\"" + text + "\"></cffunction>"); final PsiElement tag = dummyFile.getFirstChild(); assert tag != null; final CfmlAttributeNameImpl namedAttribute = PsiTreeUtil.getChildOfType(tag, CfmlAttributeNameImpl.class); assert namedAttribute != null; final PsiElement element = namedAttribute.getValueElement(); assert element != null; return element; } @Nullable public static String getPureAttributeValue(CfmlTag tag, String attributeName) { final CfmlAttributeImpl[] attributes = PsiTreeUtil.getChildrenOfType(tag, CfmlAttributeImpl.class); if (attributes == null) { return null; } for (CfmlAttributeImpl attribute : attributes) { if (attributeName.equals(attribute.getAttributeName())) { return attribute.getPureAttributeValue(); } } return null; } public static boolean isFunctionDefinition(Object element) { return element instanceof CfmlFunction || (element instanceof CfmlNamedAttributeImpl && ((CfmlNamedAttributeImpl)element).getParent() instanceof CfmlFunction); } public static CfmlFunction getFunctionDefinition(Object element) { if (element instanceof CfmlFunction) { return (CfmlFunction)element; } if (element instanceof CfmlNamedAttributeImpl && ((CfmlNamedAttributeImpl)element).getParent() instanceof CfmlFunction) { return ((CfmlFunction)((CfmlNamedAttributeImpl)element).getParent()); } return null; } @Nullable public static PsiElement getAttributeValueElement(PsiElement element, @NotNull String attributeName) { final CfmlAttributeImpl[] attributes = PsiTreeUtil.getChildrenOfType(element, CfmlAttributeImpl.class); if (attributes == null) { return null; } for (CfmlAttributeImpl attribute : attributes) { if (attributeName.equals(attribute.getAttributeName())) { return attribute.getValueElement(); } } return null; } @NotNull public static PsiReference[] getComponentReferencesFromAttributes(PsiElement element) { final PsiElement rEx = getAttributeValueElement(element, "extends"); final PsiElement rImpl = getAttributeValueElement(element, "implements"); ASTNode rExNode = rEx != null ? rEx.getNode() : null; ASTNode rImplNode = rImpl != null ? rImpl.getNode() : null; if (rExNode != null) { return rImplNode == null ? new PsiReference[]{new CfmlComponentReference(rExNode, element)} : new PsiReference[]{new CfmlComponentReference(rExNode, element), new CfmlComponentReference(rImplNode, element)}; } if (rImplNode != null) { String implList = rImplNode.getText(); if (!implList.contains(",")) { return new PsiReference[]{new CfmlComponentReference(rImplNode, element)}; }/* else { // TODO: to parse list of components }*/ } return PsiReference.EMPTY_ARRAY; } @Nullable private static ASTNode getSuperComponentNode(PsiElement element) { final PsiElement rEx = getAttributeValueElement(element, "extends"); if (rEx != null) { return rEx.getNode(); } return null; } @NotNull public static String getSuperComponentName(PsiElement element) { ASTNode superComponentNode = getSuperComponentNode(element); if (superComponentNode != null) { return superComponentNode.getText(); } return ""; } @Nullable public static CfmlComponentReference getSuperComponentReference(PsiElement element) { ASTNode node = getSuperComponentNode(element); if (node != null) { return new CfmlComponentReference(node, element); } return null; } @Nullable public static CfmlComponent getSuperComponent(PsiElement element) { CfmlComponentReference referenceToSuperComponent = getSuperComponentReference(element); if (referenceToSuperComponent != null) { PsiElement resolve = referenceToSuperComponent.resolve(); if (resolve != null && resolve instanceof CfmlComponent) { return (CfmlComponent)resolve; } } return null; } public interface Getter<T, V> { T get(V v); } private static <Result extends PsiNamedElement> Result[] componentHierarchyGatherer(CfmlComponent component, Getter<Result[], CfmlComponent> gatherer, Result[] EMPTY_ARRAY, boolean isSuperPriority) { CfmlComponent currentComponent = isSuperPriority ? component.getSuper() : component; Set<String> names = new HashSet<>(); List<Result> result = new LinkedList<>(); while (currentComponent != null) { for (Result candidate : gatherer.get(currentComponent)) { if (names.add(candidate.getName())) { result.add(candidate); } } currentComponent = currentComponent.getSuper(); } if (isSuperPriority) { currentComponent = component; for (Result candidate : gatherer.get(currentComponent)) { if (names.add(candidate.getName())) { result.add(candidate); } } } return result.toArray(EMPTY_ARRAY); } @NotNull public static CfmlFunction[] getFunctionsWithSupers(CfmlComponent component, boolean isSuperPriority) { return componentHierarchyGatherer(component, new Getter<CfmlFunction[], CfmlComponent>() { @Override public CfmlFunction[] get(CfmlComponent component) { return component.getFunctions(); } }, CfmlFunction.EMPTY_ARRAY, isSuperPriority); } @NotNull public static CfmlProperty[] getPropertiesWithSupers(CfmlComponent component, boolean isSuperPriority) { return componentHierarchyGatherer(component, new Getter<CfmlProperty[], CfmlComponent>() { @Override public CfmlProperty[] get(CfmlComponent component) { return component.getProperties(); } }, CfmlProperty.EMPTY_ARRAY, isSuperPriority); } public static boolean processGlobalVariablesForComponent(CfmlComponent component, final PsiScopeProcessor processor, final ResolveState state, final PsiElement lastParent) { boolean res = true; try { component.accept(new CfmlRecursiveElementVisitor() { public void visitCfmlAssignmentExpression(CfmlAssignmentExpression expression) { if (expression.getFirstChild().getNode().getElementType() != CfscriptTokenTypes.VAR_KEYWORD && expression.getParent() != lastParent) { if (expression.getAssignedVariable() != null && !processor.execute(expression.getAssignedVariable(), state)) { throw Stop.DONE; } } } @Override public void visitCfmlComponent(CfmlComponent component) { super.visitElement(component); } @Override public void visitCfmlFunction(CfmlFunction function) { if (function != lastParent) { // skip function we are inside super.visitCfmlFunction(function); } } @Override public void visitElement(PsiElement element) { if (element != lastParent && element instanceof CfmlAssignmentExpression) { visitCfmlAssignmentExpression((CfmlAssignmentExpression)element); } else { super.visitElement(element); } } }); } catch (CfmlRecursiveElementVisitor.Stop e) { res = false; } return res; } }