package org.radrails.rails.ui.text;
import java.io.File;
import java.io.FilenameFilter;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.radrails.rails.core.RailsLog;
import org.rubypeople.rdt.core.IMethod;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.util.Util;
public class RailsHeuristicCompletionComputer {
private static final String CONTROLLER_FILE_SUFFIX = "_controller.rb";
public static Map<String, File> getControllerCompletions(File controllersFolder, IDocument doc, int offset) {
Map<String, File> list = new HashMap<String, File>();
if (controllersFolder == null) return list;
String fullPrefix = getFullPrefix(doc, offset);
if (!looksLikeControllerCompletion(fullPrefix)) return list;
File[] controllers = controllersFolder.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(CONTROLLER_FILE_SUFFIX);
}
});
for (int i = 0; i < controllers.length; i++) {
File controller = controllers[i];
String name = controller.getName().substring(0, controller.getName().length() - CONTROLLER_FILE_SUFFIX.length());
String replacement = surroundWithQuotes(offset, doc, fullPrefix, name);
list.put(replacement, controller);
}
return list;
}
private static String surroundWithQuotes(int offset, IDocument doc, String fullPrefix, String replacement) {
// Only add these closing quotes if next char is not a closing quote
char next = 'a';
try {
if (offset < doc.getLength())
next = doc.getChar(offset);
} catch (BadLocationException e) {
// ignore
}
if (fullPrefix.endsWith("'")) {
if (next != '\'') replacement = replacement + "'";
} else if (fullPrefix.endsWith("\"")) {
if (next != '"') replacement = replacement + "\"";
} else {
replacement = "'" + replacement + "'";
}
return replacement;
}
private static boolean looksLikeControllerCompletion(String prefix) {
return Pattern.matches(".*:controller\\s*=>\\s*['|\"]?", prefix);
}
private static String getFullPrefix(IDocument doc, int offset) {
int length = 0;
String prefix = "";
try {
while((offset - length > 0) && Pattern.matches("[^\\n|^\\r|^;]", doc.get(offset - length - 1, 1))) {
length++;
}
prefix = doc.get(offset - length, length);
} catch (BadLocationException e) {
// ignore
}
return prefix;
}
public static Map<String, File> getActionCompletions(File controllersFolder, IDocument doc, int offset) {
Map<String, File> list = new HashMap<String, File>();
if (controllersFolder == null) return list;
String fullPrefix = getFullPrefix(doc, offset);
if (!looksLikeActionCompletion(fullPrefix)) return list;
// Extract the controller name
String controllerName = getControllerName(fullPrefix);
// Grab that controller
File controller = getController(controllersFolder, controllerName);
if (controller == null) return list;
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(controller.getAbsolutePath()));
IRubyScript script = RubyCore.create(file);
String typeName = Util.underscoresToCamelCase(handleNamespacing(controllerName)) + "Controller";
try {
IType type = script.getType(typeName);
if (type == null) return list;
// Grab all the public methods
IMethod[] methods = type.getMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].isPublic()) {
String name = methods[i].getElementName();
String replacement = surroundWithQuotes(offset, doc, fullPrefix, name);
list.put(replacement, controller);
}
}
} catch (RubyModelException e) {
RailsLog.log(e);
}
return list;
}
private static String handleNamespacing(String controllerName) {
String[] parts = controllerName.split("[\\|/]");
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < parts.length; i++) {
if (i > 0) buffer.append("::");
String upper = Character.toUpperCase(parts[i].charAt(0)) + parts[i].substring(1);
buffer.append(upper);
}
return buffer.toString();
}
private static File getController(File controllersFolder, String controllerName) {
String[] parts = controllerName.split("[\\|/]");
controllerName = parts[parts.length - 1];
for (int i = 0; i < parts.length - 1; i++) {
controllersFolder = new File(controllersFolder, parts[i]);
if (!controllersFolder.exists()) return null;
}
File[] controllers = controllersFolder.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(CONTROLLER_FILE_SUFFIX);
}
});
for (int i = 0; i < controllers.length; i++) {
File controller = controllers[i];
String name = controller.getName().substring(0, controller.getName().length() - CONTROLLER_FILE_SUFFIX.length());
if (name.equals(controllerName)) return controller;
}
return null;
}
private static String getControllerName(String prefix) {
Pattern pat = Pattern.compile(".*:controller\\s*=>\\s*['|\"|:]?([\\w|\\|/]+)?['|\"]?");
Matcher matcher = pat.matcher(prefix);
if (!matcher.find()) {
return null;
}
return matcher.group(1);
}
private static boolean looksLikeActionCompletion(String prefix) {
return Pattern.matches(".*:action\\s*=>\\s*['|\"]?", prefix);
}
}