com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.google.idea.blaze.base.lang.buildfile.psi.util.PsiUtils.java

Source

/*
 * Copyright 2016 The Bazel Authors. All rights reserved.
 *
 * 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.google.idea.blaze.base.lang.buildfile.psi.util;

import com.google.common.collect.Lists;
import com.google.idea.blaze.base.lang.buildfile.psi.AssignmentStatement;
import com.google.idea.blaze.base.lang.buildfile.psi.Expression;
import com.google.idea.blaze.base.lang.buildfile.psi.ReferenceExpression;
import com.google.idea.blaze.base.lang.buildfile.psi.TargetExpression;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Processor;
import java.util.List;
import javax.annotation.Nullable;

/** Utility methods for working with PSI elements */
public class PsiUtils {

    public static ASTNode createNewName(Project project, String name) {
        return BuildElementGenerator.getInstance(project).createNameIdentifier(name);
    }

    public static ASTNode createNewLabel(Project project, String labelString) {
        return BuildElementGenerator.getInstance(project).createStringNode(labelString);
    }

    @Nullable
    public static PsiElement getPreviousNodeInTree(PsiElement element) {
        PsiElement prevSibling = null;
        while (element != null && (prevSibling = element.getPrevSibling()) == null) {
            element = element.getParent();
        }
        return prevSibling != null ? lastElementInSubtree(prevSibling) : null;
    }

    /** The last element in the tree rooted at the given element. */
    public static PsiElement lastElementInSubtree(PsiElement element) {
        PsiElement lastChild;
        while ((lastChild = element.getLastChild()) != null) {
            element = lastChild;
        }
        return element;
    }

    /**
     * Walks up PSI tree, looking for a parent of the specified class. Stops searching when it reaches
     * a parent of type PsiDirectory.
     */
    @Nullable
    public static <T extends PsiElement> T getParentOfType(PsiElement element, Class<T> psiClass) {
        PsiElement parent = element.getParent();
        while (parent != null && !(parent instanceof PsiDirectory)) {
            if (psiClass.isInstance(parent)) {
                return (T) parent;
            }
            parent = parent.getParent();
        }
        return null;
    }

    @Nullable
    public static <T extends PsiElement> T findFirstChildOfClassRecursive(PsiElement parent, Class<T> psiClass) {
        List<T> holder = Lists.newArrayListWithExpectedSize(1);
        Processor<T> getFirst = t -> {
            holder.add(t);
            return false;
        };
        processChildrenOfType(parent, getFirst, psiClass);
        return holder.isEmpty() ? null : holder.get(0);
    }

    @Nullable
    public static <T extends PsiElement> T findLastChildOfClassRecursive(PsiElement parent, Class<T> psiClass) {
        List<T> holder = Lists.newArrayListWithExpectedSize(1);
        Processor<T> getFirst = t -> {
            holder.add(t);
            return false;
        };
        processChildrenOfType(parent, getFirst, psiClass, true);
        return holder.isEmpty() ? null : holder.get(0);
    }

    public static <T extends PsiElement> List<T> findAllChildrenOfClassRecursive(PsiElement parent,
            Class<T> psiClass) {
        List<T> result = Lists.newArrayList();
        processChildrenOfType(parent, new CommonProcessors.CollectProcessor(result), psiClass);
        return result;
    }

    /**
     * Walk through entire PSI tree rooted at 'element', processing all children of the given type.
     *
     * @return true if processing was stopped by the processor
     */
    public static <T extends PsiElement> boolean processChildrenOfType(PsiElement element, Processor<T> processor,
            Class<T> psiClass) {
        return processChildrenOfType(element, processor, psiClass, false);
    }

    /**
     * Walk through entire PSI tree rooted at 'element', processing all children of the given type.
     *
     * @return true if processing was stopped by the processor
     */
    private static <T extends PsiElement> boolean processChildrenOfType(PsiElement element, Processor<T> processor,
            Class<T> psiClass, boolean reverseOrder) {
        PsiElement child = reverseOrder ? element.getLastChild() : element.getFirstChild();
        while (child != null) {
            if (psiClass.isInstance(child)) {
                if (!processor.process((T) child)) {
                    return true;
                }
            }
            if (processChildrenOfType(child, processor, psiClass, reverseOrder)) {
                return true;
            }
            child = reverseOrder ? child.getPrevSibling() : child.getNextSibling();
        }
        return false;
    }

    public static TextRange childRangeInParent(TextRange parentRange, TextRange childRange) {
        return childRange.shiftRight(-parentRange.getStartOffset());
    }

    @Nullable
    public static String getFilePath(@Nullable PsiFile file) {
        VirtualFile virtualFile = file != null ? file.getVirtualFile() : null;
        return virtualFile != null ? virtualFile.getPath() : null;
    }

    /**
     * For ReferenceExpressions, follows the chain of references until it hits a
     * non-ReferenceExpression. For other types, returns the input expression.
     */
    public static PsiElement getReferencedTarget(Expression expr) {
        PsiElement element = expr;
        while (element instanceof ReferenceExpression) {
            PsiElement referencedElement = ((ReferenceExpression) element).getReferencedElement();
            if (referencedElement == null) {
                return element;
            }
            element = referencedElement;
        }
        return element;
    }

    /**
     * For ReferenceExpressions, follows the chain of references until it hits a
     * non-ReferenceExpression, then evaluates the value of that target. For other types, returns the
     * input expression.
     */
    public static PsiElement getReferencedTargetValue(Expression expr) {
        PsiElement element = getReferencedTarget(expr);
        if (element instanceof TargetExpression) {
            PsiElement parent = element.getParent();
            if (parent instanceof AssignmentStatement) {
                return ((AssignmentStatement) parent).getAssignedValue();
            }
        }
        return element;
    }
}