com.github.magicsky.sya.checkers.TestSourceReader.java Source code

Java tutorial

Introduction

Here is the source code for com.github.magicsky.sya.checkers.TestSourceReader.java

Source

/*******************************************************************************
 * Copyright (c) 2006, 2012 Wind River Systems, Inc. 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:
 *     Markus Schorn - initial API and implementation
 *     Andrew Ferguson (Symbian)
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package com.github.magicsky.sya.checkers;

import junit.framework.Assert;
import org.apache.commons.io.FileUtils;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexFileLocation;
import org.eclipse.cdt.core.index.IndexLocationFactory;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.osgi.framework.Bundle;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Utilities for reading test source code from plug-in .java sources
 */
public class TestSourceReader {
    private final Bundle bundle;
    private final String srcRoot;
    private final Class clazz;
    private final int numSections;

    /**
     * @param bundle  the bundle containing the source, if {@code null} can try to load using
     *                classpath (source folder has to be in the classpath for this to work)
     * @param srcRoot the directory inside the bundle containing the packages
     * @param clazz   the name of the class containing the test
     */
    public TestSourceReader(Bundle bundle, String srcRoot, Class clazz) {
        this(bundle, srcRoot, clazz, 0);
    }

    /**
     * @param bundle      the bundle containing the source, if {@code null} can try to load using
     *                    classpath (source folder has to be in the classpath for this to work)
     * @param srcRoot     the directory inside the bundle containing the packages
     * @param clazz       the name of the class containing the test
     * @param numSections the number of comment sections preceding the named test to return.
     *                    Pass zero to get all available sections.
     */
    public TestSourceReader(Bundle bundle, String srcRoot, Class clazz, int numSections) {
        this.bundle = bundle;
        this.srcRoot = srcRoot;
        this.clazz = clazz;
        this.numSections = numSections;
    }

    public String readTaggedComment(String tag) throws IOException {
        return readTaggedComment(bundle, tag, clazz, tag);
    }

    /**
     * Returns an array of StringBuilder objects for each comment section found preceding the named
     * test in the source code.
     *
     * @param srcRoot     the directory inside the bundle containing the packages
     * @param clazz       the name of the class containing the test
     * @param testName    the name of the test
     * @param numSections the number of comment sections preceding the named test to return.
     *                    Pass zero to get all available sections.
     * @return an array of StringBuilder objects for each comment section found preceding the named
     * test in the source code.
     * @throws IOException
     */
    public static StringBuilder[] getContentsForTest(String srcRoot, Class clazz, final String testName,
            int numSections) throws IOException {
        // Walk up the class inheritance chain until we find the test method.
        try {
            while (clazz.getMethod(testName).getDeclaringClass() != clazz) {
                clazz = clazz.getSuperclass();
            }
        } catch (SecurityException e) {
            Assert.fail(e.getMessage());
        } catch (NoSuchMethodException e) {
            Assert.fail(e.getMessage());
        }

        while (true) {
            // Find and open the .java file for the class clazz.
            String fqn = clazz.getName().replace('.', '/');
            fqn = fqn.indexOf("$") == -1 ? fqn : fqn.substring(0, fqn.indexOf("$"));
            String classFile = fqn + ".java";
            InputStream in;
            Class superclass = clazz.getSuperclass();
            try {
                in = FileUtils.openInputStream(new File(srcRoot + '/' + classFile));
            } catch (IOException e) {
                if (superclass == null || !superclass.getPackage().equals(clazz.getPackage())) {
                    throw e;
                }
                clazz = superclass;
                continue;
            }

            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            try {
                // Read the java file collecting comments until we encounter the test method.
                List<StringBuilder> contents = new ArrayList<StringBuilder>();
                StringBuilder content = new StringBuilder();
                for (String line = br.readLine(); line != null; line = br.readLine()) {
                    line = line.replaceFirst("^\\s*", ""); // Replace leading whitespace, preserve trailing
                    if (line.startsWith("//")) {
                        content.append(line.substring(2) + "\n");
                    } else {
                        if (!line.startsWith("@") && content.length() > 0) {
                            contents.add(content);
                            if (numSections > 0 && contents.size() == numSections + 1)
                                contents.remove(0);
                            content = new StringBuilder();
                        }
                        if (line.length() > 0 && !contents.isEmpty()) {
                            int idx = line.indexOf(testName);
                            if (idx != -1
                                    && !Character.isJavaIdentifierPart(line.charAt(idx + testName.length()))) {
                                return contents.toArray(new StringBuilder[contents.size()]);
                            }
                            if (!line.startsWith("@")) {
                                contents.clear();
                            }
                        }
                    }
                }
            } finally {
                br.close();
            }

            if (superclass == null || !superclass.getPackage().equals(clazz.getPackage())) {
                throw new IOException("Test data not found for " + clazz.getName() + "." + testName);
            }
            clazz = superclass;
        }
    }

    /**
     * Searches for the offset of the first occurrence of a string in a workspace file.
     *
     * @param lookfor  string to be searched for
     * @param fullPath full path of the workspace file
     * @return the offset or -1
     * @throws Exception
     * @throws UnsupportedEncodingException
     * @since 4.0
     */
    public static int indexOfInFile(String lookfor, Path fullPath) throws Exception {
        IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(fullPath);
        Reader reader = new BufferedReader(new InputStreamReader(file.getContents(), file.getCharset()));
        Assert.assertTrue(lookfor.indexOf('\n') == -1);
        try {
            int c = 0;
            int offset = 0;
            StringBuilder buf = new StringBuilder();
            while ((c = reader.read()) >= 0) {
                buf.append((char) c);
                if (c == '\n') {
                    int idx = buf.indexOf(lookfor);
                    if (idx >= 0) {
                        return idx + offset;
                    }
                    offset += buf.length();
                    buf.setLength(0);
                }
            }
            int idx = buf.indexOf(lookfor);
            if (idx >= 0) {
                return idx + offset;
            }
            return -1;
        } finally {
            reader.close();
        }
    }

    public static int getLineNumber(int offset, Path fullPath) throws Exception {
        IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(fullPath);
        Reader reader = new BufferedReader(new InputStreamReader(file.getContents(), file.getCharset()));
        try {
            int line = 1;
            for (int i = 0; i < offset; i++) {
                int c = reader.read();
                Assert.assertTrue(c >= 0);
                if (c == '\n')
                    line++;
            }
            return line;
        } finally {
            reader.close();
        }
    }

    /**
     * Reads a section in comments form the source of the given class. The section
     * is started with '// {tag}' and ends with the first line not started by '//'
     *
     * @since 4.0
     */
    public static String readTaggedComment(Bundle bundle, String srcRoot, Class clazz, final String tag)
            throws IOException {
        IPath filePath = new Path(srcRoot + '/' + clazz.getName().replace('.', '/') + ".java");

        InputStream in = FileLocator.openStream(bundle, filePath, false);
        LineNumberReader reader = new LineNumberReader(new InputStreamReader(in));
        boolean found = false;
        final StringBuilder content = new StringBuilder();
        try {
            String line = reader.readLine();
            while (line != null) {
                line = line.trim();
                if (line.startsWith("//")) {
                    line = line.substring(2);
                    if (found) {
                        content.append(line);
                        content.append('\n');
                    } else {
                        line = line.trim();
                        if (line.startsWith("{" + tag)) {
                            if (line.length() == tag.length() + 1
                                    || !Character.isJavaIdentifierPart(line.charAt(tag.length() + 1))) {
                                found = true;
                            }
                        }
                    }
                } else if (found) {
                    break;
                }
                line = reader.readLine();
            }
        } finally {
            reader.close();
        }
        Assert.assertTrue("Tag '" + tag + "' is not defined inside of '" + filePath + "'.", found);
        return content.toString();
    }

    /**
     * Creates a file with content at the given path inside the given container.
     * If the file exists its content is replaced.
     *
     * @param container a container to create the file in
     * @param filePath  the path relative to the container to create the file at
     * @param contents  the content for the file
     * @return a file object.
     * @throws CoreException
     * @since 4.0
     */
    public static IFile createFile(final IContainer container, final IPath filePath, final CharSequence contents)
            throws CoreException {
        final IWorkspace ws = ResourcesPlugin.getWorkspace();
        final IFile result[] = new IFile[1];
        ws.run(new IWorkspaceRunnable() {
            @Override
            public void run(IProgressMonitor monitor) throws CoreException {
                // Obtain file handle
                IFile file = container.getFile(filePath);

                InputStream stream;
                try {
                    stream = new ByteArrayInputStream(contents.toString().getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    throw new CoreException(new Status(IStatus.ERROR, CTestPlugin.PLUGIN_ID, null, e));
                }
                // Create file input stream
                if (file.exists()) {
                    long timestamp = file.getLocalTimeStamp();
                    file.setContents(stream, false, false, new NullProgressMonitor());
                    if (file.getLocalTimeStamp() == timestamp) {
                        file.setLocalTimeStamp(timestamp + 1000);
                    }
                } else {
                    createFolders(file);
                    file.create(stream, true, new NullProgressMonitor());
                }
                result[0] = file;
            }

            private void createFolders(IResource res) throws CoreException {
                IContainer container = res.getParent();
                if (!container.exists() && container instanceof IFolder) {
                    createFolders(container);
                    ((IFolder) container).create(true, true, new NullProgressMonitor());
                }
            }
        }, null);
        return result[0];
    }

    /**
     * Creates a file with content at the given path inside the given container.
     * If the file exists its content is replaced.
     *
     * @param container a container to create the file in
     * @param filePath  the path relative to the container to create the file at
     * @param contents  the content for the file
     * @return a file object.
     * @since 4.0
     */
    public static IFile createFile(IContainer container, String filePath, String contents) throws CoreException {
        return createFile(container, new Path(filePath), contents);
    }

    /**
     * Waits until the given file is indexed. Fails if this does not happen within the
     * given time.
     *
     * @param file
     * @param maxmillis
     * @throws Exception
     * @since 4.0
     */
    public static void waitUntilFileIsIndexed(IIndex index, IFile file, int maxmillis) throws Exception {
        long fileTimestamp = file.getLocalTimeStamp();
        IIndexFileLocation indexFileLocation = IndexLocationFactory.getWorkspaceIFL(file);

        long endTime = System.currentTimeMillis() + maxmillis;
        int timeLeft = maxmillis;
        while (timeLeft >= 0) {
            Assert.assertTrue(CCorePlugin.getIndexManager().joinIndexer(timeLeft, new NullProgressMonitor()));
            index.acquireReadLock();
            try {
                IIndexFile[] files = index.getFiles(ILinkage.CPP_LINKAGE_ID, indexFileLocation);
                if (files.length > 0 && areAllFilesNotOlderThan(files, fileTimestamp)) {
                    Assert.assertTrue(
                            CCorePlugin.getIndexManager().joinIndexer(timeLeft, new NullProgressMonitor()));
                    return;
                }
                files = index.getFiles(ILinkage.C_LINKAGE_ID, indexFileLocation);
                if (files.length > 0 && areAllFilesNotOlderThan(files, fileTimestamp)) {
                    Assert.assertTrue(
                            CCorePlugin.getIndexManager().joinIndexer(timeLeft, new NullProgressMonitor()));
                    return;
                }
            } finally {
                index.releaseReadLock();
            }

            Thread.sleep(50);
            timeLeft = (int) (endTime - System.currentTimeMillis());
        }
        Assert.fail("Indexing of " + file.getFullPath() + " did not complete in " + maxmillis / 1000. + " sec");
    }

    private static boolean areAllFilesNotOlderThan(IIndexFile[] files, long timestamp) throws CoreException {
        for (IIndexFile file : files) {
            if (file.getTimestamp() < timestamp) {
                return false;
            }
        }
        return true;
    }

    public static IASTTranslationUnit createIndexBasedAST(IIndex index, ICProject project, IFile file)
            throws CModelException, CoreException {
        ICElement elem = project.findElement(file.getFullPath());
        if (elem instanceof ITranslationUnit) {
            ITranslationUnit tu = (ITranslationUnit) elem;
            return tu.getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS);
        }
        Assert.fail("Could not create AST for " + file.getFullPath());
        return null;
    }
}