Package ccw

Source Code of ccw.ClojureCore

/*******************************************************************************
* Copyright (c) 2009 Casey Marshall 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:
*    Casey Marshall - initial API and implementation
*******************************************************************************/
/*
* Adapted from the...
**     ________ ___   / /  ___     Scala Plugin for Eclipse             **
**    / __/ __// _ | / /  / _ |    (c) 2004-2005, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |                                         **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
*
* by Casey Marshall for the Clojure plugin, ccw
\*                                                                      */

// Created on 2004-10-25 by Thierry Monney
//$Id: ScalaCore.java,v 1.3 2006/02/03 12:41:22 mcdirmid Exp $
package ccw;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.debug.core.sourcelookup.containers.LocalFileStorage;
import org.eclipse.debug.core.sourcelookup.containers.ZipEntryStorage;
import org.eclipse.jdt.core.IJarEntryResource;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.debug.ui.LocalFileStorageEditorInput;
import org.eclipse.jdt.internal.debug.ui.ZipEntryStorageEditorInput;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.javaeditor.JarEntryEditorInput;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;

import ccw.editors.clojure.ClojureEditor;
import ccw.repl.REPLView;
import ccw.util.ClojureInvoker;
import clojure.lang.RT;
import clojure.lang.Var;

/**
* This class acts as a Facade for all SDT core functionality.
*
*/
public final class ClojureCore {
 
  private static ClojureInvoker staticAnalysis = ClojureInvoker.newInvoker(CCWPlugin.getDefault(), "paredit.static-analysis");
  static public final String NATURE_ID = "ccw.nature";
    /**
     * Clojure file extension
     */
    static public final String CLOJURE_FILE_EXTENSION = "clj";

    /**
     * Clojure source file filter (used to avoid the Java builder to copy
     * Clojure sources to the output folder
     */
    static public final String CLOJURE_FILE_FILTER = "*."
            + CLOJURE_FILE_EXTENSION;

    static final Map<IProject, ClojureProject> clojureProjects = new HashMap<IProject, ClojureProject>();

    static final Map<IProject, IJavaProject> javaProjects = new HashMap<IProject, IJavaProject>();

    /**
     * Gets the SDT core preferences
     *
     * @return the preferences root node
     */
    public Preferences getPreferences() {
        return CCWPlugin.getDefault().getPluginPreferences();
    }

    private static boolean addNature(IProject project, String natureID) {
        IProjectDescription desc;
        try {
            desc = project.getDescription();
        }
        catch (CoreException e) {
            CCWPlugin.logError("Could not get project description", e);
            return false;
        }
        String[] ids = desc.getNatureIds();
        String[] newIDs = new String[ids.length + 1];
        System.arraycopy(ids, 0, newIDs, 1, ids.length);
        newIDs[0] = natureID;
        desc.setNatureIds(newIDs);
        try {
            project.setDescription(desc, null);
        }
        catch (CoreException e) {
            CCWPlugin.logError("Could not set project description", e);
            return false;
        }
        return true;
    }

    /**
     * Adds tha Scala nature to the given project
     *
     * @param project
     *            the project
     * @return <code>true</code> if the nature was correctly added,
     *         <code>false</code> otherwise
     */
    public static boolean addClojureNature(IProject project) {
        return addNature(project, NATURE_ID);
    }

    /**
     * Adds tha Java nature to the given project
     *
     * @param project
     *            the project
     * @return <code>true</code> if the nature was correctly added,
     *         <code>false</code> otherwise
     */
    public static boolean addJavaNature(IProject project) {
        return addNature(project, JavaCore.NATURE_ID);
    }

    // public static final String[] SCALA_JARS = getScalaJars();

    private static final String EXCLUSION_FILTER_ID = "org.eclipse.jdt.core.builder.resourceCopyExclusionFilter";

    /**
     * Gets the Java project associated to the given project
     *
     * @param project
     *            the Eclipse project
     * @return the associated Java project
     */
    public static IJavaProject getJavaProject(IProject project) {
        if (project == null)
            return null;
        try {
            if (!project.exists()
                || !project.isOpen()
                    || !project.hasNature(NATURE_ID))
                return null;
        }
        catch (CoreException e) {
            CCWPlugin.logError(e);
            return null;
        }
        IJavaProject p = (IJavaProject) javaProjects.get(project);
        if (p == null) {
            p = JavaCore.create(project);
            javaProjects.put(project, p);
        }
        return p;
    }

    /**
     * Gets the Clojure project associated to the given project
     *
     * @param project
     *            the Eclipse project
     * @return the associated Scala project
     */
    public static ClojureProject getClojureProject(IProject project) {
        ClojureProject p = clojureProjects.get(project);
        if (p != null)
            return p;
        try {
            if (!project.exists()
                || !project.isOpen()
                    || !project.hasNature(NATURE_ID))
                return null;
        }
        catch (CoreException e) {
            CCWPlugin.logError(e);
            return null;
        }
        p = new ClojureProject(project);
        return p;
    }

    /**
     * Gets all the Clojure projects in the workspace
     *
     * @return an array containing all the Scala projects
     */
    public static ClojureProject[] getClojureProjects() {
        return clojureProjects.values().toArray(new ClojureProject[] {});
    }
   
  /*
     *  TODO Still 1 more case to handle:
     *  - when a LIBRARY does not have source file in its classpath, then search attached source files folder/archive
     */
    public static void openInEditor(String searchedNS, String searchedFileName, int line) {
    try {
        REPLView replView = REPLView.activeREPL.get();
        if (replView != null) {
          String projectName = replView.getLaunch().getLaunchConfiguration().getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
            openInEditor(searchedNS, searchedFileName, line, projectName, false);
        }
    } catch (CoreException e) {
      CCWPlugin.logError("error while trying to obtain project's name from configuration, while trying to show source file of a symbol", e);
    }
  }
   
    /**
     * Tries to open a clojure file in an editor
     * @return an editor input if the file has been found, or null
     */
    private static IEditorInput findEditorInput(IPackageFragmentRoot packageFragmentRoot, IPackageFragment packageFragment, String searchedPackage, String searchedFileName) throws JavaModelException {
      if (packageFragment.exists()
          && packageFragment.getElementName().equals(searchedPackage)) {
        for (Object njr: packageFragment.isDefaultPackage() ? packageFragmentRoot.getNonJavaResources() : packageFragment.getNonJavaResources()) {
          if (njr instanceof IJarEntryResource) {
            IJarEntryResource jer = (IJarEntryResource) njr;
            if (jer.getName().equals(searchedFileName)) {
              return new JarEntryEditorInput(jer);
            }
          } else if (njr instanceof IFile) {
            IFile file = (IFile) njr;
            if (file.getName().equals(searchedFileName)) {
              return new FileEditorInput(file);
            }
          } else if (njr instanceof File) {
            File f = (File) njr;
            if (f.getName().equals(searchedFileName)) {
              IFileStore fileStore = EFS.getLocalFileSystem().getStore(f.toURI());
              return new FileStoreEditorInput(fileStore);
            }
          }
        }
      }
      return null;
    }

    /**
     * Tries to open a clojure file in an editor
     * @return an editor input if the file has been found, or null
     */
    private static IEditorInput findEditorInput(
        IPackageFragmentRoot packageFragmentRoot,
        String searchedPackage,
        String searchedFileName)
            throws JavaModelException {
     
      // Find in package fragment
      IPackageFragment packageFragment = packageFragmentRoot.getPackageFragment(searchedPackage);
     
    IEditorInput editorInput = findEditorInput(packageFragmentRoot,
                                           packageFragment,
                                           searchedPackage,
                                           searchedFileName);
    if (editorInput != null) {
      return editorInput;
    }
     
      return findEditorInputInSourceAttachment(
            packageFragmentRoot,
            searchedPackage,
            searchedFileName);
    }

  private static IEditorInput findEditorInputInSourceAttachment(
      IPackageFragmentRoot packageFragmentRoot,
      String searchedPackage,
      String searchedFileName) throws JavaModelException {
   
      final IPath sourceAttachmentPath = packageFragmentRoot.getSourceAttachmentPath();
     
      if (sourceAttachmentPath == null) {
        return null;
      }
     
      final String searchedPath = searchedPackage.replaceAll("\\.", "/");
   
    final IResource workspaceResource = ResourcesPlugin.getWorkspace().getRoot().findMember(sourceAttachmentPath);
   
    // Find in workspace
    if (workspaceResource != null) {
      if (workspaceResource.getType() == IResource.FOLDER) {
        IFolder folder = (IFolder) workspaceResource;
        IFile r = (IFile) folder.findMember(searchedPath + "/" + searchedFileName);
        if (r != null && r.exists()) {
          return new FileEditorInput(r);
        }
      } else {
        // Don't know what to do here
      }
    }
   
   
    // Find outside workspace or in archive
    final IPath sourceAbsolutePath = toOSAbsoluteIPath(sourceAttachmentPath);
   
    final File sourceFile = sourceAbsolutePath.toFile();
    if (!sourceFile.exists()) {
      CCWPlugin.logWarning("sourceFile " + sourceFile + " does not exist form sourceAttachmentPath " + sourceAttachmentPath);
      // Nothing can be done
    } else if (sourceFile.isDirectory()) {
      final File maybeSourceFile = sourceAbsolutePath.append(searchedPath + "/" + searchedFileName).toFile();
      if (maybeSourceFile.exists()) {
        return new LocalFileStorageEditorInput(
            new LocalFileStorage(maybeSourceFile));
      } else {
        // Nothing, alas
      }
    } else {
      ZipFile zipFile;
      try {
        zipFile = new JarFile(sourceFile, true, JarFile.OPEN_READ);
        ZipEntry zipEntry = zipFile.getEntry(searchedPath + "/" + searchedFileName);
        if (zipEntry != null) {
          return new ZipEntryStorageEditorInput(
              new ZipEntryStorage(zipFile, zipEntry));
        } else {
          // Nothing, alas
        }
      } catch (IOException e) {
        CCWPlugin.logError("Error trying to open " + sourceAbsolutePath, e);
      }
    }
    return null;
  }

    /**
     * File name, without extension
     */
    private static String getFileName(final String path) {
      return ( path.contains("/") ) 
          ? path.substring(1 + path.lastIndexOf('/'))
          : path;
    }
   
    private static boolean openInEditor(final String searchedNS,
                                    final String initialSearchedFileName,
                                    final int line,
                                    final String projectName,
                                    final boolean onlyExportedEntries)
                                        throws PartInitException {
     
      if (initialSearchedFileName == null) {
        return false;
      }
     
      final String searchedFileName = getFileName(initialSearchedFileName);
     
    final IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
   
    final IJavaProject javaProject = JavaCore.create(project);
   
    try {
      System.out.println("search file name : " + searchedFileName);
      System.out.println("searched ns : " + searchedNS);
     
      final String searchedPackage = namespaceToPackage(searchedNS);
      System.out.println("searched package: " + searchedPackage);
     
      for (IPackageFragmentRoot packageFragmentRoot: javaProject.getAllPackageFragmentRoots()) {
       
        final IEditorInput editorInput =
            findEditorInput(packageFragmentRoot,
                        searchedPackage,
                        searchedFileName);
       
        if (editorInput != null) {
          IEditorPart editor = IDE.openEditor(CCWPlugin.getActivePage(), editorInput, ClojureEditor.ID);
          gotoEditorLine(editor, line);
          return true;
        } else {
          continue;
        }
      }
    } catch (JavaModelException e) {
      e.printStackTrace();
    }
    return false;
  }

  private static String namespaceToPackage(final String searchedNS) {
    String packagePart = (searchedNS.contains(".")) ? searchedNS.substring(0, searchedNS.lastIndexOf(".")) : "";
   
    return packagePart.replace('-', '_');
  }
   
    public static IPath toOSAbsoluteIPath(IPath path) {
    if (ClojureCore.isWorkspaceRelativeIPath(path)) {
      boolean isFolder = path.getFileExtension() == null;
      if (isFolder) {
        path = ResourcesPlugin.getWorkspace().getRoot().getFolder(path).getLocation();
      } else {
        path = ResourcesPlugin.getWorkspace().getRoot().getFile(path).getLocation();
      }
    }
    return path;
    }
   
    public static boolean isWorkspaceRelativeIPath(IPath path) {
      return ResourcesPlugin.getWorkspace().getRoot().exists(path);
  }

  public static String getPackageNameFromNamespaceName(String nsName) {
      return (nsName.lastIndexOf(".") < 0) ? "" : nsName.substring(0, nsName.lastIndexOf(".")).replace('-', '_');
    }

  public static String getNamespaceNameFromPackageName(String packageName) {
    return packageName.toString().replace('/', '.').replace('_', '-');
  }
 
  private final static Pattern SEARCH_DECLARING_NAMESPACE_PATTERN
    = Pattern.compile("\\(\\s*(?:in-)?ns\\s+([^\\s\\)#\\[\\'\\{]+)");

  public static String findDeclaringNamespace(String sourceText) {
    Var sexp = RT.var("paredit.parser", "sexp");
    try {
      return (String) findDeclaringNamespace((Map) sexp.invoke(sourceText));
    } catch (Exception e) {
      return null;
    }
  }
  public static String findDeclaringNamespace(Map tree) {
    try {
      return (String) staticAnalysis._("find-namespace", tree);
    } catch (Exception e) {
      CCWPlugin.logError("exception while trying to find declaring namespace for " + tree, e);
      return null;
    }
  }
 
  private final static Pattern HAS_NS_CALL_PATTERN = Pattern.compile("^\\s*\\(ns(\\s.*|$)", Pattern.MULTILINE);
  /**
   * @return true if a ns call is detected by a regex-based heuristic
   */
  private static boolean hasNsCall(String sourceCode) {
    Matcher matcher = HAS_NS_CALL_PATTERN.matcher(sourceCode);
    return matcher.find();
  }

  /**
   * Get the file's namespace name if the file is a lib.
   * <p>
   * Checks:
   * <ul>
   *   <li>if the file is in the classpath</li>
   *   <li>if the file ends with .clj</li>
   *   <li>if the file contains a ns call(*)</li>
   * </ul>
   * If check is ko, returns nil, the file does not correspond to a 'lib'
   * <br/>
   * If check is ok, deduce the 'lib' name from the file path, e.g. a file
   * path such as <code>/projectName/src/foo/bar_baz/core.clj</code> will
   * return "foo.bar-baz.core".
   * </p>
   * <p>
   * (*): based on a simplistic regex based heuristic for maximum speed
   * </p>
   * @param file
   * @return null if not a lib, String with lib namespace name if a lib
   */
    public static String findMaybeLibNamespace(IFile file) {
      try {
        IJavaProject jProject = JavaCore.create(file.getProject());
        IPackageFragmentRoot[] froots = jProject.getAllPackageFragmentRoots();
        for (IPackageFragmentRoot froot: froots) {
          if (froot.getPath().isPrefixOf(file.getFullPath())) {
            String ret = findMaybeLibNamespace(file, froot.getPath());
            if (ret != null) {
              return ret;
            } else {
              continue;
            }
          }
        }
    } catch (JavaModelException e) {
      CCWPlugin.logError("unable to determine the fragment root of the file " + file, e);
    }
    return null;
    }
   
    /**
     * @return starting with a leading slash "/", the root classpath relative
     *         path of this file
     */
    public static String getAsRootClasspathRelativePath(IFile file) {
      try {
        IJavaProject jProject = JavaCore.create(file.getProject());
        IPackageFragmentRoot[] froots = jProject.getAllPackageFragmentRoots();
        for (IPackageFragmentRoot froot: froots) {
          if (froot.getPath().isPrefixOf(file.getFullPath())) {
            String ret = "/" + file.getFullPath().makeRelativeTo(froot.getPath()).toString();
            return ret;
          }
        }
    } catch (JavaModelException e) {
      CCWPlugin.logError("unable to determine the fragment root of the file " + file, e);
    }
    return null;
    }
   
    /**
     * @see <code>findMaybeLibNamespace(IFile file)</code>
     */
    public static String findMaybeLibNamespace(IFile file, IPath sourcePath) {
      if (!CLOJURE_FILE_EXTENSION.equals(file.getFileExtension())) {
        return null;
      }
    IPath path = file.getFullPath().removeFirstSegments(sourcePath.segmentCount()).removeFileExtension();
    if (path == null) {
      // file is not on the classpath
      return null;
    } else {
      String sourceCode = getFileText(file);
      if (hasNsCall(sourceCode)) {
//        System.out.println("path.toPortableString()" + path.toPortableString());
        return getNamespaceNameFromPackageName(path.toPortableString());
      } else {
//        System.out.println("did not find a ns call in source code of " + file);
        return null;
      }
    }
    }
   
    private static String getFileText(IFile file) {
      try {
      return (String) RT.var("clojure.core", "slurp").invoke(file.getLocation().toOSString());
    } catch (Exception e) {
      CCWPlugin.logError("error while getting text from file " + file, e);
      return null;
    }
    }
   
    private static boolean tryNonJavaResources(Object[] nonJavaResources, String searchedFileName, int line) throws PartInitException {
    for (Object nonJavaResource: nonJavaResources) {
      String nonJavaResourceName = null;
      if (IFile.class.isInstance(nonJavaResource)) {
        nonJavaResourceName = ((IFile) nonJavaResource).getName();
      } else if (IJarEntryResource.class.isInstance(nonJavaResource)) {
        nonJavaResourceName = ((IJarEntryResource) nonJavaResource).getName();
      }
      if (searchedFileName.equals(nonJavaResourceName)) {
        IEditorPart editor = EditorUtility.openInEditor(nonJavaResource);
        gotoEditorLine(editor, line);
        return true;
      }
    }
    return false;
    }

    /**
     * If line == -1 => goto last line
     */
  public static void gotoEditorLine(Object editor, int line) {
    if (ITextEditor.class.isInstance(editor)) {
      ITextEditor textEditor = (ITextEditor) editor;
      IRegion lineRegion;
      try {
        if (line == -1) {
          line = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()).getNumberOfLines();
        }
        lineRegion = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()).getLineInformation(line - 1);
        textEditor.selectAndReveal(lineRegion.getOffset(), lineRegion.getLength());
      } catch (BadLocationException e) {
        // TODO popup for a feedback to the user ?
        CCWPlugin.logError("unable to select line " + line + " in the file", e);
      }
    }
  }

}
TOP

Related Classes of ccw.ClojureCore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.