/**
* @author SwedaKonsult
*
* This file is part of MyRobotLab (http://myrobotlab.org).
*
* MyRobotLab 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 2 of the License, or
* (at your option) any later version (subject to the "Classpath" exception
* as provided in the LICENSE.txt file that accompanied this code).
*
* MyRobotLab is distributed in the hope that it will be useful or fun,
* 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.
*
* All libraries in thirdParty bundle are subject to their own license
* requirements - please refer to http://myrobotlab.org/libraries for
* details.
*
* Enjoy !
*
*/
package org.myrobotlab.ui.autocomplete;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import org.fife.ui.autocomplete.BasicCompletion;
import org.fife.ui.autocomplete.Completion;
import org.myrobotlab.control.widget.JavaCompletionProvider;
import org.myrobotlab.logging.LoggerFactory;
import org.myrobotlab.reflection.Locator;
import org.myrobotlab.service.GUIService;
import org.slf4j.Logger;
/**
* @author SwedaKonsult
*
*/
public class MRLCompletionProvider extends JavaCompletionProvider {
/**
* Logger for this guy.
*/
public final static Logger log = LoggerFactory.getLogger(GUIService.class.getCanonicalName());
/**
* Create the modifiers HTML string that will be used in the GUIService.
*
* @param modifiers
* @return empty string if there are no modifiers that meet our criteria
*/
private CharSequence buildModifiersDescriptionForGUI(int modifiers) {
if (modifiers <= 0) {
return "";
}
StringBuffer modifiersDescription = new StringBuffer();
if (Modifier.isStatic(modifiers)) {
modifiersDescription.append("<li>").append("static").append("</li>");
}
if (Modifier.isSynchronized(modifiers)) {
modifiersDescription.append("<li>").append("synchronized").append("</li>");
}
if (modifiersDescription.length() == 0) {
return "";
}
modifiersDescription.insert(0, "<br><br><b><i>Modifiers:</i></b><ul>").append("</ul>");
return modifiersDescription;
}
/**
* Get the class name part of the full descriptor.
*
* @param fullClassName
* the class name including package name.
* @return the class name parsed out of the full name.
*/
private CharSequence getClassName(String fullClassName) {
return fullClassName.subSequence(fullClassName.lastIndexOf('.') + 1, fullClassName.length());
}
/**
* Load all constants/static fields available and set them up with the class
* name prefixing the variable name in order to be able to suggest when the
* class name is typed in.
*
* @param implementation
*/
private void loadClassFields(Class<?> implementation) {
if (implementation == null) {
return;
}
Field[] fields = implementation.getDeclaredFields();
if (fields == null || fields.length == 0) {
return;
}
Completion completer;
StringBuffer genericsString = new StringBuffer();
String fieldTypeName;
String fullClassName = implementation.getName();
String className = getClassName(fullClassName).toString();
// log.error(className);
if (!(className.contains("$"))) {
for (Field f : fields) {
if (f.getName() == "main" || !Modifier.isPublic(f.getModifiers())) {// ||
// !Modifier.isStatic(f.getModifiers()))
// {
continue;
}
// TODO: grab the generics for this field
genericsString.delete(0, genericsString.length());
fieldTypeName = f.getType().getName();
completer = new BasicCompletion(this, String.format("%s.%s", className, f.getName()), getClassName(fieldTypeName).toString(),
String.format("<html><body>" + "<b>%1$s %2$s.%3$s" + "%4$s" + "</b> %5$s</body></html>", fieldTypeName, className, f.getName(), genericsString,
buildModifiersDescriptionForGUI(f.getModifiers())));
addCompletion(completer);
}
}
completer = null;
}
/**
* Helper method that recurses implementation to find all public methods
* declared.
*
* @param implementation
* the class to analyze
*/
private void loadClassMethods(Class<?> implementation) {
if (implementation == null) {
return;
}
Method[] methods = implementation.getDeclaredMethods();
if (methods == null || methods.length == 0) {
return;
}
Completion completer;
int arrayLength = 0;
int loop = 0;
Class<?>[] params;
StringBuffer paramsString = new StringBuffer();
StringBuffer shortParamsString = new StringBuffer();
StringBuffer genericsString = new StringBuffer();
String matchString, extraString;
String fullClassName = implementation.getName();
String className = getClassName(fullClassName).toString();
boolean isStatic;
if (!(className.contains("$"))) {
for (Method m : methods) {
if (m.getName() == "main" || !Modifier.isPublic(m.getModifiers())) {
continue;
}
paramsString.delete(0, paramsString.length());
params = m.getParameterTypes();
arrayLength = params.length;
isStatic = Modifier.isStatic(m.getModifiers());
if (isStatic) {
shortParamsString.delete(0, shortParamsString.length());
}
if (arrayLength > 0) {
for (loop = 0; loop < arrayLength; loop++) {
if (loop > 0) {
paramsString.append(",");
}
if (isStatic) {
shortParamsString.append(getClassName(params[loop].getName()));
}
paramsString.append(params[loop].getName());
// TODO: should grab the generics for each parameter
}
}
genericsString.delete(0, genericsString.length());
// if (isStatic) {
matchString = String.format("%s.%s(", getClassName(fullClassName), m.getName());
extraString = shortParamsString.toString();
/*
* } else { matchString = String.format(".%s(", m.getName());
* extraString = m.getName(); }
*/
// TODO: grab the generics for this method
completer = new BasicCompletion(this, matchString, extraString,
String.format("<html><body>" + "<b>%1$s %2$s.%3$s" + "%5$s(%4$s)" + "</b> %6$s <br> <a href=\"http://myrobotlab.org/service/%2$s\">more help</a> </body></html>",
m.getReturnType().getName(), className, m.getName(), paramsString, genericsString, buildModifiersDescriptionForGUI(m.getModifiers())));
addCompletion(completer);
}
}
completer = null;
}
/**
* Overriding base class declaration in order to load methods that should be
* easy to find and use in Python. Still calls out the base class in order to
* load the Java keywords.
*/
@Override
protected void loadCompletions() {
super.loadCompletions();
try {
loadInformationFromClasses(Locator.getClasses("org.myrobotlab.service"));
} catch (IOException e) {
log.error("Could not load MRLCompletions because of I/O issues.", e);
}
}
/**
* Load all information we want for the UI from the class.
*
* @param implementation
*/
private void loadInformationFromClass(Class<?> implementation) {
loadClassMethods(implementation);
loadClassFields(implementation);
}
/**
* Load everything from the classes in the list.
*
* @param classList
*/
private void loadInformationFromClasses(List<Class<?>> classList) {
if (classList == null || classList.size() == 0) {
return;
}
for (Class<?> c : classList) {
loadInformationFromClass(c);
}
}
}