package org.radrails.rails.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.radrails.rails.internal.core.RailsPlugin;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
public abstract class RailsConventions {
private static final String APP = "app";
private static final String RB = ".rb";
private static final String MODELS = "models";
private static final String VIEWS = "views";
private static final String CONTROLLERS = "controllers";
private static final String HELPERS = "helpers";
private static final String TEST = "test";
private static final String FUNCTIONAL = "functional";
private static final String UNIT = "unit";
private static final String CONTROLLER_SUFFIX = "_controller";
private static final String CONTROLLER_FILE_SUFFIX = CONTROLLER_SUFFIX + RB;
private static final String TEST_SUFFIX = "_test";
private static final String FUNCTIONAL_TEST_SUFFIX = CONTROLLER_SUFFIX + TEST_SUFFIX + RB;
private static final String UNIT_TEST_SUFFIX = TEST_SUFFIX + RB;
private static final String HELPER_SUFFIX = "_helper";
private static final String HELPER_FILE_SUFFIX = HELPER_SUFFIX + RB;
public static IFile getModelFromController(IFile controllerFile) {
if (!looksLikeController(controllerFile)) return null; // if we're not in a controller, just return
IPath controllerFilePath = controllerFile.getProjectRelativePath();
String controllerFilename = controllerFilePath.lastSegment();
IPath model = getModelFromController(controllerFilePath, getModelName(controllerFilename));
IFile file = controllerFile.getProject().getFile(model);
if (!file.exists()) return null;
return file;
}
public static IFile getControllerFromModel(IFile modelFile) {
IPath modelFilePath = modelFile.getProjectRelativePath();
String modelFilename = modelFilePath.lastSegment();
if (!looksLikeModel(modelFile)) return null;
String singular = modelFilename.substring(0, modelFilename.indexOf('.'));
String plural = Inflector.pluralize(singular);
String controllerName = plural + CONTROLLER_FILE_SUFFIX;
IPath controllerPath = buildController(getAppFolderFromModel(modelFilePath), controllerName);
IFile file = modelFile.getProject().getFile(controllerPath);
if (!file.exists()) return null;
return file;
}
private static String getModelName(String controllerFilename) {
String singular = Inflector.singularize(getControllerBaseName(controllerFilename));
singular += RB;
return singular;
}
private static IPath getModelFromController(IPath controllerFilePath, String singular) {
return getAppFolderFromController(controllerFilePath).append(MODELS).append(singular);
}
private static IPath getModelFromHelper(IPath helperFilePath, String singular) {
return getAppFolderFromHelper(helperFilePath).append(MODELS).append(singular);
}
private static String getControllerBaseName(String controllerFilename) {
return controllerFilename.substring(0, controllerFilename.length() - CONTROLLER_FILE_SUFFIX.length());
}
public static IFile getModelFromView(IFile viewFile) {
if (!looksLikeView(viewFile)) return null; // if we're not in a view, just return
IPath viewFilePath = viewFile.getProjectRelativePath();
IPath model = getModelFromView(viewFilePath);
return viewFile.getProject().getFile(model);
}
public static boolean looksLikeView(IFile file) {
if (file == null) return false;
IPath railsRoot = RailsPlugin.findRailsRoot(file.getProject());
if (!railsRoot.isPrefixOf(file.getProjectRelativePath())) return false;
IPath afterRoot = file.getProjectRelativePath().removeFirstSegments(railsRoot.segmentCount());
return afterRoot.segment(0).equals(APP) && afterRoot.segment(1).equals(VIEWS);
}
private static String getModelNameFromViewPath(IPath viewFilePath) {
String singular = Inflector.singularize(getPluralResourceNameFromViewPath(viewFilePath));
singular += RB;
return singular;
}
private static String getPluralResourceNameFromViewPath(IPath viewFilePath) {
IFile viewFile = ResourcesPlugin.getWorkspace().getRoot().getFile(viewFilePath);
IPath railsRoot = RailsPlugin.findRailsRoot(viewFile.getProject());
IPath appViews = railsRoot.append(APP).append(VIEWS);
return viewFilePath.removeFirstSegments(appViews.segmentCount()).removeLastSegments(1).toPortableString();
}
private static IPath getModelFromView(IPath viewFilePath) {
String singular = getModelNameFromViewPath(viewFilePath);
return getAppFolderFromView(viewFilePath).append(MODELS).append(singular);
}
public static IFile getControllerFromView(IFile viewFile) {
if (!looksLikeView(viewFile)) return null;
IPath railsRoot = RailsPlugin.findRailsRoot(viewFile.getProject());
IPath viewFilePath = viewFile.getProjectRelativePath();
String plural = getPluralResourceNameFromViewPath(viewFilePath);
String[] namespace = getControllerNamespace(viewFilePath);
String controllerName = new Path(plural).lastSegment() + CONTROLLER_FILE_SUFFIX;
IPath controllerPath = railsRoot.append(APP).append(CONTROLLERS);
for (int i = 0; i < namespace.length; i++) {
controllerPath = controllerPath.append(namespace[i]);
}
controllerPath = controllerPath.append(controllerName);
IFile file = viewFile.getProject().getFile(controllerPath);
if (!file.exists()) return null;
return file;
}
public static IType getControllerTypeFromViewFile(IFile viewFile) {
try
{
IFile controllerFile = RailsConventions.getControllerFromView(viewFile);
if (controllerFile == null)
return null;
IRubyScript controllerScript = RubyCore.create(controllerFile);
if (controllerScript == null)
return null;
IType[] types = controllerScript.getTypes();
if (types == null || types.length == 0)
return null;
return types[0];
}
catch (RubyModelException e)
{
RailsLog.logError(e.getMessage(), e);
}
return null;
}
private static IPath buildController(IPath appFolder, String controllerName) {
return appFolder.append(CONTROLLERS).append(controllerName);
}
private static IPath getAppFolderFromView(IPath viewFilePath) {
String[] segments = viewFilePath.segments();
for (int i = 0; i < segments.length; i++)
{
if (segments[i].equals(APP))
{
return viewFilePath.uptoSegment(i + 1);
}
}
return viewFilePath.removeLastSegments(3);
}
private static IPath getAppFolderFromController(IPath controllerFilePath) {
return controllerFilePath.removeLastSegments(2);
}
private static IPath getAppFolderFromModel(IPath modelFilePath) {
return modelFilePath.removeLastSegments(2);
}
public static IFile getFunctionalTestFromView(IFile viewFile) {
if (!looksLikeView(viewFile)) return null;
IPath viewFilePath = viewFile.getProjectRelativePath();
String plural = getPluralResourceNameFromViewPath(viewFilePath);
IPath functionalTestPath = buildFunctionalTest(getAppFolderFromView(viewFilePath), plural);
return viewFile.getProject().getFile(functionalTestPath);
}
private static IPath buildFunctionalTest(IPath appFolder, String plural) {
return appFolder.removeLastSegments(1).append(TEST).append(FUNCTIONAL).append(plural + FUNCTIONAL_TEST_SUFFIX);
}
public static boolean looksLikeController(IFile currentFile) {
if (currentFile == null) return false;
String name = currentFile.getName();
if (name.endsWith(CONTROLLER_FILE_SUFFIX)) return true;
return false;
}
public static boolean looksLikeHelper(IFile currentFile) {
if (currentFile == null) return false;
String name = currentFile.getName();
if (name.endsWith(HELPER_FILE_SUFFIX)) return true;
return false;
}
public static IFile getHelperFromModel(IFile modelFile) {
IPath modelFilePath = modelFile.getProjectRelativePath();
String modelFilename = modelFilePath.lastSegment();
if (!looksLikeModel(modelFile)) return null;
String singular = modelFilename.substring(0, modelFilename.indexOf('.'));
String plural = Inflector.pluralize(singular);
String helperName = plural + HELPER_FILE_SUFFIX;
IPath helperPath = buildHelper(getAppFolderFromModel(modelFilePath), helperName);
IFile file = modelFile.getProject().getFile(helperPath);
if (!file.exists()) return null;
return file;
}
private static IPath buildHelper(IPath appFolder, String helperName) {
return appFolder.append(HELPERS).append(helperName);
}
public static IFile getHelperFromView(IFile viewFile) {
if (!looksLikeView(viewFile)) return null;
IPath viewFilePath = viewFile.getProjectRelativePath();
String plural = getPluralResourceNameFromViewPath(viewFilePath);
IPath helperPath = buildHelper(getAppFolderFromView(viewFilePath), plural + HELPER_FILE_SUFFIX);
IFile file = viewFile.getProject().getFile(helperPath);
if (!file.exists()) return null;
return file;
}
public static IFile getHelperFromController(IFile controllerFile) {
if (!looksLikeController(controllerFile)) return null; // if we're not in a controller, just return
IPath controllerFilePath = controllerFile.getProjectRelativePath();
String controllerFilename = controllerFilePath.lastSegment();
String plural = getControllerBaseName(controllerFilename);
String helperName = plural + HELPER_FILE_SUFFIX;
IPath helperPath = buildHelper(getAppFolderFromController(controllerFilePath), helperName);
IFile file = controllerFile.getProject().getFile(helperPath);
if (!file.exists()) return null;
return file;
}
public static IFile getControllerFromHelper(IFile helperFile) {
if (!looksLikeHelper(helperFile)) return null;
IPath helperFilePath = helperFile.getProjectRelativePath();
String plural = getPluralResourceNameFromHelperPath(helperFilePath.lastSegment());
String controllerName = plural + CONTROLLER_FILE_SUFFIX;
IPath controllerPath = buildController(getAppFolderFromHelper(helperFilePath), controllerName);
IFile file = helperFile.getProject().getFile(controllerPath);
if (!file.exists()) return null;
return file;
}
private static String getPluralResourceNameFromHelperPath(String helperFileName) {
return helperFileName.substring(0, helperFileName.length() - HELPER_FILE_SUFFIX.length());
}
private static IPath getAppFolderFromHelper(IPath helperFilePath) {
return helperFilePath.removeLastSegments(2);
}
public static IFile getModelFromHelper(IFile helperFile) {
if (!looksLikeHelper(helperFile)) return null; // if we're not in a helper, just return
IPath helperFilePath = helperFile.getProjectRelativePath();
String helperFilename = helperFilePath.lastSegment();
String modelName = Inflector.singularize(getPluralResourceNameFromHelperPath(helperFilename))+ RB;
IPath model = getModelFromHelper(helperFilePath, modelName);
IFile file = helperFile.getProject().getFile(model);
if (!file.exists()) return null;
return file;
}
public static IFile getControllerFromFunctionalTest(IFile currentFile) {
if (!looksLikeFunctionalTest(currentFile)) return null; // if we're not in a functional test, just return
IPath helperFilePath = currentFile.getProjectRelativePath();
String filename = helperFilePath.lastSegment();
String[] namespace = getControllerNamespace(helperFilePath);
IPath railsRoot = RailsPlugin.findRailsRoot(currentFile.getProject());
String controllerName = filename.substring(0, filename.length() - (TEST_SUFFIX + RB).length()) + RB;
IPath controller = railsRoot.append(APP).append(CONTROLLERS);
for (int i = 0; i < namespace.length; i++) {
controller = controller.append(namespace[i]);
}
controller = controller.append(controllerName);
IFile file = currentFile.getProject().getFile(controller);
if (!file.exists()) return null;
return file;
}
private static String[] getControllerNamespace(IPath helperFilePath) {
String[] segments = helperFilePath.segments();
List<String> namespace = new ArrayList<String>();
for (int i = segments.length - 2; i >=0; i--) {
if (segments[i].equals(VIEWS)) {
namespace.remove(0);
break;
}
if (segments[i].equals(FUNCTIONAL) || segments[i].equals(CONTROLLERS)) {
break;
}
namespace.add(segments[i]);
}
Collections.reverse(namespace);
return namespace.toArray(new String[namespace.size()]);
}
private static boolean looksLikeFunctionalTest(IFile currentFile) {
IPath railsRoot = RailsPlugin.findRailsRoot(currentFile.getProject());
if (!railsRoot.isPrefixOf(currentFile.getProjectRelativePath())) return false;
IPath relativeToRailsRoot = currentFile.getProjectRelativePath().removeFirstSegments(railsRoot.segmentCount());
if (!relativeToRailsRoot.segment(0).equals(TEST)) return false;
if (!relativeToRailsRoot.segment(1).equals(FUNCTIONAL)) return false;
return relativeToRailsRoot.lastSegment().endsWith(TEST_SUFFIX + RB);
}
public static IFile getControllerFromUnitTest(IFile currentFile) {
if (!looksLikeUnitTest(currentFile)) return null; // if we're not in a unit test, just return
String fileName = currentFile.getProjectRelativePath().lastSegment();
IPath railsRoot = RailsPlugin.findRailsRoot(currentFile.getProject());
String singularModel = fileName.substring(0, fileName.length() - (TEST_SUFFIX + RB).length());
String controllerName = Inflector.pluralize(singularModel) + CONTROLLER_FILE_SUFFIX;
IPath controller = railsRoot.append(APP).append(CONTROLLERS).append(controllerName);
IFile file = currentFile.getProject().getFile(controller);
if (!file.exists()) return null;
return file;
}
private static boolean looksLikeUnitTest(IFile currentFile) {
IPath railsRoot = RailsPlugin.findRailsRoot(currentFile.getProject());
if (!railsRoot.isPrefixOf(currentFile.getProjectRelativePath())) return false;
IPath relativeToRailsRoot = currentFile.getProjectRelativePath().removeFirstSegments(railsRoot.segmentCount());
if (!relativeToRailsRoot.segment(0).equals(TEST)) return false;
if (!relativeToRailsRoot.segment(1).equals(UNIT)) return false;
return relativeToRailsRoot.lastSegment().endsWith(TEST_SUFFIX + RB);
}
public static IFile getModelFromFunctionalTest(IFile currentFile) {
if (!looksLikeFunctionalTest(currentFile)) return null; // if we're not in a functional test, just return
IPath filePath = currentFile.getProjectRelativePath();
String fileName = filePath.lastSegment();
for (int i = 0; i < filePath.segmentCount(); i++)
{
if (filePath.segment(i).equals(FUNCTIONAL))
{
fileName = filePath.removeFirstSegments(i + 1).toPortableString();
break;
}
}
String pluralModelName = fileName.substring(0, fileName.length() - (FUNCTIONAL_TEST_SUFFIX).length());
String modelName = Inflector.singularize(pluralModelName) + RB;
IPath model = new Path(APP).append(MODELS).append(modelName);
IFile file = currentFile.getProject().getFile(model);
if (!file.exists()) return null;
return file;
}
public static IFile getModelFromUnitTest(IFile currentFile) {
if (!looksLikeUnitTest(currentFile)) return null; // if we're not in a unit test, just return
IPath filePath = currentFile.getProjectRelativePath();
String fileName = filePath.lastSegment();
String modelName = fileName.substring(0, fileName.length() - (TEST_SUFFIX + RB).length()) + RB;
IPath model = new Path(APP).append(MODELS).append(modelName);
IFile file = currentFile.getProject().getFile(model);
if (!file.exists()) return null;
return file;
}
public static IFile getHelperFromFunctionalTest(IFile currentFile) {
if (!looksLikeFunctionalTest(currentFile)) return null; // if we're not in a functional test, just return
IPath filePath = currentFile.getProjectRelativePath();
String fileName = filePath.lastSegment();
IPath railsRoot = RailsPlugin.findRailsRoot(currentFile.getProject());
String pluralModelName = fileName.substring(0, fileName.length() - (FUNCTIONAL_TEST_SUFFIX).length());
String helperName = pluralModelName + HELPER_FILE_SUFFIX;
IPath helper = railsRoot.append(APP).append(HELPERS).append(helperName);
IFile file = currentFile.getProject().getFile(helper);
if (!file.exists()) return null;
return file;
}
public static IFile getHelperFromUnitTest(IFile currentFile) {
if (!looksLikeUnitTest(currentFile)) return null; // if we're not in a unit test, just return
IPath filePath = currentFile.getProjectRelativePath();
String fileName = filePath.lastSegment();
String modelName = fileName.substring(0, fileName.length() - (TEST_SUFFIX + RB).length());
String helperName = Inflector.pluralize(modelName) + HELPER_FILE_SUFFIX;
IPath helper = new Path(APP).append(HELPERS).append(helperName);
IFile file = currentFile.getProject().getFile(helper);
if (!file.exists()) return null;
return file;
}
public static boolean looksLikeModel(IFile currentFile) {
if (currentFile == null) return false;
IPath path = currentFile.getProjectRelativePath();
int index = findAppIndex(path);
if (index == -1) return false;
if (index + 2 > path.segmentCount()) return false; // need at least 2 more segments past "app" ("models", and model file)
String appSubfolder = path.segment(index + 1);
if (!appSubfolder.equals(MODELS)) return false;
String modelFilename = path.lastSegment();
return modelFilename.endsWith(RB);
// TODO Check the type contained for subtype of ActiveRecord::Base?
}
public static boolean looksLikeTest(IFile currentFile) {
if (currentFile == null) return false;
IPath path = currentFile.getProjectRelativePath();
int index = findTestIndex(path);
if (index == -1) return false;
if (index + 2 > path.segmentCount()) return false; // need at least 2 more segments past "test" ("unit"/"functional", and test file)
String appSubfolder = path.segment(index + 1);
if (appSubfolder.equals(UNIT)) {
return path.lastSegment().endsWith(UNIT_TEST_SUFFIX);
}
if (appSubfolder.equals(FUNCTIONAL)) {
return path.lastSegment().endsWith(FUNCTIONAL_TEST_SUFFIX);
}
return false;
}
private static int findAppIndex(IPath path) {
return findSegmentIndex(path, APP);
}
private static int findTestIndex(IPath path) {
return findSegmentIndex(path, TEST);
}
private static int findSegmentIndex(IPath path, String segmentToFind) {
for (int i = 0; i < path.segmentCount(); i++) {
String segment = path.segment(i);
if (segment.equals(segmentToFind)) {
return i;
}
}
return -1;
}
}