org.eclim.plugin.jdt.util.JavaUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.eclim.plugin.jdt.util.JavaUtils.java

Source

/**
 * Copyright (C) 2005 - 2013  Eric Van Dewoestine
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.eclim.plugin.jdt.util;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.VFS;

import org.eclim.Services;

import org.eclim.plugin.core.preference.Preferences;

import org.eclim.plugin.core.project.ProjectNatureFactory;

import org.eclim.plugin.core.util.ProjectUtils;

import org.eclim.plugin.jdt.PluginResources;

import org.eclim.util.IOUtils;

import org.eclim.util.file.FileUtils;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;

import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageDeclaration;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.Signature;

import org.eclipse.jdt.core.compiler.IProblem;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Javadoc;

import org.eclipse.jdt.core.formatter.IndentManipulation;

import org.eclipse.jdt.internal.core.DocumentAdapter;

import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;

import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;

import org.eclipse.jdt.internal.formatter.DefaultCodeFormatter;

import org.eclipse.jdt.internal.ui.JavaPlugin;

import org.eclipse.jdt.internal.ui.text.correction.ContributedProcessorDescriptor;

import org.eclipse.jdt.ui.JavaUI;

import org.eclipse.jdt.ui.text.java.IQuickAssistProcessor;
import org.eclipse.jdt.ui.text.java.IQuickFixProcessor;

import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;

import org.eclipse.text.edits.TextEdit;

/**
 * Utility methods for working with java files / projects.
 *
 * @author Eric Van Dewoestine
 */
public class JavaUtils {
    /**
     * String version of java.lang package.
     */
    public static final String JAVA_LANG = "java.lang";

    private static final Pattern PACKAGE_LINE = Pattern.compile("^\\s*package\\s+(\\w+(\\.\\w+){0,})\\s*;\\s*$");

    private static final Pattern TRAILING_WHITESPACE = Pattern.compile("[ \t]+$", Pattern.MULTILINE);

    private static ContributedProcessorDescriptor[] correctionProcessors;
    private static ContributedProcessorDescriptor[] assistProcessors;

    /**
     * Gets a java project by name.
     *
     * @param project The name of the project.
     * @return The project.
     */
    public static IJavaProject getJavaProject(String project) throws Exception {
        return getJavaProject(ProjectUtils.getProject(project, true));
    }

    /**
     * Gets a java project from the supplied IProject.
     *
     * @param project The IProject.
     * @return The java project.
     */
    public static IJavaProject getJavaProject(IProject project) throws Exception {
        if (ProjectUtils.getPath(project) == null) {
            throw new IllegalArgumentException(Services.getMessage("project.location.null", project.getName()));
        }

        if (!project.hasNature(PluginResources.NATURE)) {
            throw new IllegalArgumentException(Services.getMessage("project.missing.nature", project.getName(),
                    ProjectNatureFactory.getAliasForNature(PluginResources.NATURE)));
        }

        IJavaProject javaProject = JavaCore.create(project);
        if (javaProject == null || !javaProject.exists()) {
            throw new IllegalArgumentException(Services.getMessage("project.not.found", project));
        }

        return javaProject;
    }

    /**
     * Finds a compilation unit by looking in the java project of the supplied
     * name.
     *
     * @param project The name of the project to locate the file in.
     * @param file The file to find.
     * @return The compilation unit.
     *
     * @throws IllegalArgumentException If the file is not found.
     */
    public static ICompilationUnit getCompilationUnit(String project, String file) throws Exception {
        IJavaProject javaProject = getJavaProject(project);
        return getCompilationUnit(javaProject, file);
    }

    /**
     * Finds a compilation unit by looking in all the available java projects.
     *
     * @param file The absolute file path to find.
     * @return The compilation unit.
     *
     * @throws IllegalArgumentException If the file is not found.
     */
    public static ICompilationUnit getCompilationUnit(String file) throws Exception {
        IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
        for (int ii = 0; ii < projects.length; ii++) {
            IJavaProject javaProject = getJavaProject(projects[ii]);

            ICompilationUnit src = JavaCore
                    .createCompilationUnitFrom(ProjectUtils.getFile(javaProject.getProject(), file));
            if (src != null && src.exists()) {
                return src;
            }
        }
        throw new IllegalArgumentException(Services.getMessage("src.file.not.found", file, ".classpath"));
    }

    /**
     * Gets the compilation unit from the supplied project.
     *
     * @param project The project.
     * @param file The absolute path to the file.
     * @return The compilation unit or null if not found.
     *
     * @throws IllegalArgumentException If the file is not found.
     */
    public static ICompilationUnit getCompilationUnit(IJavaProject project, String file) throws Exception {
        ICompilationUnit src = JavaCore.createCompilationUnitFrom(ProjectUtils.getFile(project.getProject(), file));
        if (src == null || !src.exists()) {
            throw new IllegalArgumentException(Services.getMessage("src.file.not.found", file, ".classpath"));
        }
        return src;
    }

    /**
     * Finds a compilation unit by looking in all the java project of the supplied
     * name.
     *
     * @param project The name of the project to locate the file in.
     * @param file The src dir relative file path to find.
     * @return The compilation unit or null if not found.
     */
    public static ICompilationUnit findCompilationUnit(String project, String file) throws Exception {
        IPath path = Path.fromOSString(file);

        IJavaProject javaProject = getJavaProject(project);
        javaProject.open(null);
        //javaProject.getResource().refreshLocal(IResource.DEPTH_INFINITE, null);

        ICompilationUnit src = (ICompilationUnit) javaProject.findElement(path);

        return src;
    }

    /**
     * Finds a compilation unit by looking in all the available java projects.
     *
     * @param file The src directory relative file to find.
     * @return The compilation unit or null if not found.
     */
    public static ICompilationUnit findCompilationUnit(String file) throws Exception {
        IPath path = Path.fromOSString(file);
        IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
        for (IProject project : projects) {
            if (project.hasNature(JavaCore.NATURE_ID)) {
                IJavaProject javaProject = getJavaProject(project);
                javaProject.open(null);
                //javaProject.getResource().refreshLocal(IResource.DEPTH_INFINITE, null);

                ICompilationUnit src = (ICompilationUnit) javaProject.findElement(path);
                if (src != null) {
                    return src;
                }
            }
        }
        return null;
    }

    /**
     * Attempts to locate the IClassFile for the supplied file path from the
     * specified project's classpath.
     *
     * @param project The project to find the class file in.
     * @param path Absolute path or url (jar:, zip:) to a .java source file.
     * @return The IClassFile.
     */
    public static IClassFile findClassFile(IJavaProject project, String path) throws Exception {
        if (path.startsWith("/") || path.toLowerCase().startsWith("jar:")
                || path.toLowerCase().startsWith("zip:")) {
            FileSystemManager fsManager = VFS.getManager();
            FileObject file = fsManager.resolveFile(path);
            if (file.exists()) {
                BufferedReader in = null;
                try {
                    in = new BufferedReader(new InputStreamReader(file.getContent().getInputStream()));
                    String pack = null;
                    String line = null;
                    while ((line = in.readLine()) != null) {
                        Matcher matcher = PACKAGE_LINE.matcher(line);
                        if (matcher.matches()) {
                            pack = matcher.group(1);
                            break;
                        }
                    }
                    if (pack != null) {
                        String name = pack + '.' + FileUtils.getFileName(file.getName().getPath());
                        IType type = project.findType(name);
                        if (type != null) {
                            return type.getClassFile();
                        }
                    }
                } finally {
                    IOUtils.closeQuietly(in);
                }
            }
        }
        return null;
    }

    /**
     * Gets the primary element (compilation unit or class file) for the supplied
     * element.
     *
     * @param element The element.
     * @return The primary element.
     */
    public static IJavaElement getPrimaryElement(IJavaElement element) {
        IJavaElement parent = element;
        while (parent.getElementType() != IJavaElement.COMPILATION_UNIT
                && parent.getElementType() != IJavaElement.CLASS_FILE) {
            parent = parent.getParent();
        }
        return parent;
    }

    /**
     * Get the offset of the supplied element within the source.
     *
     * @param element The element
     * @return The offset or -1 if it could not be determined.
     */
    public static int getElementOffset(IJavaElement element) throws Exception {
        IJavaElement parent = getPrimaryElement(element);
        CompilationUnit cu = null;
        switch (parent.getElementType()) {
        case IJavaElement.COMPILATION_UNIT:
            cu = ASTUtils.getCompilationUnit((ICompilationUnit) parent);
            break;
        case IJavaElement.CLASS_FILE:
            try {
                cu = ASTUtils.getCompilationUnit((IClassFile) parent);
            } catch (IllegalStateException ise) {
                // no source attachement
            }
            break;
        }

        if (cu != null) {
            ASTNode[] nodes = ASTNodeSearchUtil.getDeclarationNodes(element, cu);
            if (nodes != null && nodes.length > 0) {
                int offset = nodes[0].getStartPosition();
                if (nodes[0] instanceof BodyDeclaration) {
                    Javadoc docs = ((BodyDeclaration) nodes[0]).getJavadoc();
                    if (docs != null) {
                        offset += docs.getLength() + 1;
                    }
                }
                return offset;
            }
        }
        return -1;
    }

    /**
     * Gets the IDocument for the supplied src file.
     * <p/>
     * Code borrowed from org.eclipse.jdt.internal.core.JavaModelOperation.
     *
     * @param src The src file.
     * @return The IDocument.
     */
    public static IDocument getDocument(ICompilationUnit src) throws Exception {
        IBuffer buffer = src.getBuffer();
        if (buffer instanceof IDocument) {
            return (IDocument) buffer;
        }
        return new DocumentAdapter(buffer);
    }

    /**
     * Gets the fully qualified name of the supplied java element.
     * <p/>
     * NOTE: For easy of determining fields and method segments, they are appended
     * with a javadoc style '#' instead of the normal '.'.
     *
     * @param element The IJavaElement.
     *
     * @return The fully qualified name.
     */
    public static String getFullyQualifiedName(IJavaElement element) {
        IJavaElement parent = element;
        while (parent.getElementType() != IJavaElement.COMPILATION_UNIT
                && parent.getElementType() != IJavaElement.CLASS_FILE) {
            parent = parent.getParent();
        }

        StringBuffer elementName = new StringBuffer().append(parent.getParent().getElementName()).append('.')
                .append(FileUtils.getFileName(parent.getElementName()));

        switch (element.getElementType()) {
        case IJavaElement.FIELD:
            IField field = (IField) element;
            elementName.append('#').append(field.getElementName());
            break;
        case IJavaElement.METHOD:
            IMethod method = (IMethod) element;
            elementName.append('#').append(method.getElementName()).append('(');
            String[] parameters = method.getParameterTypes();
            for (int ii = 0; ii < parameters.length; ii++) {
                if (ii != 0) {
                    elementName.append(", ");
                }
                elementName.append(Signature.toString(parameters[ii]).replace('/', '.'));
            }
            elementName.append(')');
            break;
        }

        return elementName.toString();
    }

    /**
     * Constructs a compilation unit relative name for the supplied type.
     * <p/>
     * If the type is imported, in java.lang, or in the same package as the source
     * file, then the type name returned is unqualified, otherwise the name
     * returned is the fully qualified type name.
     *
     * @param src The compilation unit.
     * @param type The type.
     *
     * @return The relative type name.
     */
    public static String getCompilationUnitRelativeTypeName(ICompilationUnit src, IType type) throws Exception {
        String typeName = type.getFullyQualifiedName().replace('$', '.');
        if (JavaUtils.containsImport(src, type)) {
            typeName = type.getElementName();

            int parentType = type.getParent().getElementType();
            if (parentType == IJavaElement.TYPE) {
                typeName = type.getParent().getElementName() + '.' + typeName;
            } else if (parentType == IJavaElement.CLASS_FILE) {
                String parentName = type.getParent().getElementName();
                int index = parentName.indexOf('$');
                if (index != -1) {
                    parentName = parentName.substring(0, index);
                    typeName = parentName + '.' + typeName;
                }
            }
        } else {
            typeName = type.getFullyQualifiedName().replace('$', '.');
        }

        return typeName;
    }

    /**
     * Determines if the supplied src file contains an import for the
     * supplied type (including wildcard .* imports).
     *
     * @param src The compilation unit.
     * @param type The type.
     * @return true if the src file has a qualifying import.
     */
    public static boolean containsImport(ICompilationUnit src, String type) throws Exception {
        return containsImport(src, src.getType(type));
    }

    /**
     * Determines if the supplied src file contains an import for the
     * supplied type (including wildcard .* imports).
     *
     * @param src The compilation unit.
     * @param type The type.
     * @return true if the src file has a qualifying import.
     */
    public static boolean containsImport(ICompilationUnit src, IType type) throws Exception {
        String typePkg = type.getPackageFragment().getElementName();

        IPackageDeclaration[] packages = src.getPackageDeclarations();
        String pkg = packages.length > 0 ? packages[0].getElementName() : null;

        // classes in same package are auto imported.
        if ((pkg == null && typePkg == null) || (pkg != null && pkg.equals(typePkg))) {
            return true;
        }

        // java.lang is auto imported.
        if (JAVA_LANG.equals(typePkg)) {
            return true;
        }

        typePkg = typePkg + ".*";
        String typeName = type.getFullyQualifiedName().replace('$', '.');

        IImportDeclaration[] imports = src.getImports();
        for (int ii = 0; ii < imports.length; ii++) {
            String name = imports[ii].getElementName();
            if (name.equals(typeName) || name.equals(typePkg)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Format a region in the supplied source file.
     *
     * @param src The ICompilationUnit.
     * @param kind The kind of code snippet to format.
     * @param offset The starting offset of the region to format.
     * @param length The length of the region to format.
     */
    public static void format(ICompilationUnit src, int kind, int offset, int length) throws Exception {
        IBuffer buffer = src.getBuffer();
        String contents = buffer.getContents();
        String delimiter = StubUtility.getLineDelimiterUsed(src);
        DefaultCodeFormatter formatter = new DefaultCodeFormatter(src.getJavaProject().getOptions(true));

        // when the eclipse indent settings differ from vim (tabs vs spaces) then
        // the inserted method's indent may be a bit off. this is a workaround to
        // force reformatting of the code from the start of the line to the start of
        // the next set of code following the new method. Doesn't quite fix indent
        // formatting of methods in nested classes.
        while (offset > 0 && !IndentManipulation.isLineDelimiterChar(buffer.getChar(offset - 1))) {
            offset--;
            length++;
        }
        while ((offset + length) < contents.length()
                && IndentManipulation.isLineDelimiterChar(buffer.getChar(offset + length))) {
            length++;
        }

        TextEdit edits = formatter.format(kind, contents, offset, length, 0, delimiter);
        if (edits != null) {
            int oldLength = contents.length();
            Document document = new Document(contents);
            edits.apply(document);

            String formatted = document.get();

            // jdt formatter can introduce trailing whitespace (javadoc comments), so
            // we'll remove all trailing whitespace from the formatted section.
            length += formatted.length() - oldLength;
            if (offset < (offset + length)) {
                String pre = formatted.substring(0, offset);
                StringBuffer section = new StringBuffer(formatted.substring(offset, offset + length));
                StringBuffer post = new StringBuffer(formatted.substring(offset + length));
                // account for section not ending at a line delimiter
                while (!section.toString().endsWith(delimiter) && post.length() > 0) {
                    section.append(post.charAt(0));
                    post.deleteCharAt(0);
                }

                Matcher matcher = TRAILING_WHITESPACE.matcher(section);
                String stripped = matcher.replaceAll(StringUtils.EMPTY);

                src.getBuffer().setContents(pre + stripped + post);
            } else {
                src.getBuffer().setContents(formatted);
            }

            if (src.isWorkingCopy()) {
                src.commitWorkingCopy(true, null);
            }
            src.save(null, false);
        }
    }

    /**
     * Gets the java version for which all source is to be compatable with.
     *
     * @param project The java project.
     * @return The source compliance version.
     */
    public static String getCompilerSourceCompliance(IJavaProject project) {
        return (String) project.getOptions(true).get(JavaCore.COMPILER_SOURCE);
    }

    /**
     * Sets the java version for which all source is to be compatable with.
     *
     * @param version The java version.
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static void setCompilerSourceCompliance(String version) throws Exception {
        Map<String, String> options = JavaCore.getOptions();
        options.put(JavaCore.COMPILER_SOURCE, version);
        options.put(JavaCore.COMPILER_COMPLIANCE, version);
        options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, version);

        JavaCore.setOptions((Hashtable) options);
    }

    /**
     * Sets the java version for which all source is to be compatable with.
     *
     * @param project The java project.
     * @param version The java version.
     */
    @SuppressWarnings("unchecked")
    public static void setCompilerSourceCompliance(IJavaProject project, String version) throws Exception {
        // using project.setOption(String,String) doesn't save the setting.
        Map<String, String> options = project.getOptions(false);
        options.put(JavaCore.COMPILER_SOURCE, version);
        options.put(JavaCore.COMPILER_COMPLIANCE, version);
        options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, version);

        project.setOptions(options);
    }

    /**
     * Loads the supplied map to be used in a template with the available
     * preferences.
     *
     * @param project The current project.
     * @param preferences The eclim preferences.
     * @param values The values to populate.
     */
    public static void loadPreferencesForTemplate(IProject project, Preferences preferences,
            Map<String, Object> values) throws Exception {
        Map<String, String> options = preferences.getValues(project);
        for (String key : options.keySet()) {
            String value = options.get(key);
            values.put(key.replace('.', '_'), value);
        }
    }

    /**
     * Gets the problems for a given src file.
     *
     * @param src The src file.
     * @return The problems.
     */
    public static IProblem[] getProblems(ICompilationUnit src) throws Exception {
        return getProblems(src, null);
    }

    /**
     * Gets the problems for a given src file.
     *
     * @param src The src file.
     * @param ids Array of problem ids to accept.
     * @return The problems.
     */
    public static IProblem[] getProblems(ICompilationUnit src, int[] ids) throws Exception {
        ICompilationUnit workingCopy = src.getWorkingCopy(null);

        ProblemRequestor requestor = new ProblemRequestor(ids);
        try {
            workingCopy.discardWorkingCopy();
            workingCopy.becomeWorkingCopy(requestor, null);
        } finally {
            workingCopy.discardWorkingCopy();
        }
        List<IProblem> problems = requestor.getProblems();
        return (IProblem[]) problems.toArray(new IProblem[problems.size()]);
    }

    /**
     * Gets array of IQuickFixProcessor(s).
     * <p/>
     * Based on
     * org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor#getCorrectionProcessors()
     *
     * @param src The src file to get processors for.
     * @return quick fix processors.
     */
    public static IQuickFixProcessor[] getQuickFixProcessors(ICompilationUnit src) throws Exception {
        if (correctionProcessors == null) {
            correctionProcessors = getProcessorDescriptors("quickFixProcessors", true);
        }
        IQuickFixProcessor[] processors = new IQuickFixProcessor[correctionProcessors.length];
        for (int ii = 0; ii < correctionProcessors.length; ii++) {
            processors[ii] = (IQuickFixProcessor) correctionProcessors[ii].getProcessor(src,
                    IQuickFixProcessor.class);
        }
        return processors;
    }

    /**
     * Gets array of IQuickAssistProcessor(s).
     * <p/>
     * Based on
     * org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor#getAssistProcessors()
     *
     * @param src The src file to get processors for.
     * @return quick assist processors.
     */
    public static IQuickAssistProcessor[] getQuickAssistProcessors(ICompilationUnit src) throws Exception {
        if (assistProcessors == null) {
            assistProcessors = getProcessorDescriptors("quickAssistProcessors", false);
        }
        IQuickAssistProcessor[] processors = new IQuickAssistProcessor[assistProcessors.length];
        for (int ii = 0; ii < assistProcessors.length; ii++) {
            processors[ii] = (IQuickAssistProcessor) assistProcessors[ii].getProcessor(src,
                    IQuickAssistProcessor.class);
        }
        return processors;
    }

    private static ContributedProcessorDescriptor[] getProcessorDescriptors(String id, boolean testMarkerTypes)
            throws Exception {
        IConfigurationElement[] elements = Platform.getExtensionRegistry()
                .getConfigurationElementsFor(JavaUI.ID_PLUGIN, id);
        ArrayList<ContributedProcessorDescriptor> res = new ArrayList<ContributedProcessorDescriptor>(
                elements.length);

        for (int ii = 0; ii < elements.length; ii++) {
            ContributedProcessorDescriptor desc = new ContributedProcessorDescriptor(elements[ii], testMarkerTypes);
            IStatus status = desc.checkSyntax();
            if (status.isOK()) {
                res.add(desc);
            } else {
                JavaPlugin.log(status);
            }
        }
        return (ContributedProcessorDescriptor[]) res.toArray(new ContributedProcessorDescriptor[res.size()]);
    }

    /**
     * Gathers problems as a src file is processed.
     */
    public static class ProblemRequestor implements org.eclipse.jdt.core.IProblemRequestor {
        private ArrayList<IProblem> problems = new ArrayList<IProblem>();
        private int[] ids;

        /**
         * Constructs a new instance.
         *
         * @param ids Array of problem ids to accept.
         */
        public ProblemRequestor(int[] ids) {
            this.ids = ids;
        }

        /**
         * Gets a list of problems recorded.
         *
         * @return The list of problems.
         */
        public List<IProblem> getProblems() {
            return problems;
        }

        /**
         * {@inheritDoc}
         */
        public void acceptProblem(IProblem problem) {
            if (ids != null) {
                for (int ii = 0; ii < ids.length; ii++) {
                    if (problem.getID() == ids[ii]) {
                        problems.add(problem);
                        break;
                    }
                }
            } else {
                problems.add(problem);
            }
        }

        /**
         * {@inheritDoc}
         */
        public void beginReporting() {
        }

        /**
         * {@inheritDoc}
         */
        public void endReporting() {
        }

        /**
         * {@inheritDoc}
         */
        public boolean isActive() {
            return true;
        }
    }
}