package csep.parser;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.antlr.runtime.Token;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.parsetree.reconstr.Serializer;
import com.aptana.editor.coffee.parsing.lexer.CoffeeSymbol;
import com.google.inject.Guice;
import csep.CoffeeScriptRuntimeModule;
import csep.parser.antlr.internal.InternalCoffeeScriptLexer;
/**
* Used mostly for debugging
*
* @author Adam Schmideg <adam@schmideg.net>
*/
public class Helper {
public final static String INDENT = " ";
private static Serializer serializer;
public final static Set<String> IGNORE_PROP_NAMES = new HashSet<String>(
Arrays.asList(new String[] { "class", "operator" }));
public static Serializer getSerializer() {
if (serializer == null) {
serializer = Guice.createInjector(new CoffeeScriptRuntimeModule())
.getInstance(Serializer.class);
}
return serializer;
}
/**
* Create a human-friendly string representation of a parse element
*
* @param eobj
* @return
*/
public static String stringify(EObject eobj) {
return stringify(eobj, INDENT);
}
protected static String stringify(Object obj, String indent) {
if (obj == null) {
return "null";
}
StringBuffer buf = new StringBuffer();
Class<?> clazz = obj.getClass();
Map<String, Object> props = getProperties(obj);
Object maybeOperator = props.get("operator");
String name = clazz.getSimpleName();
if (maybeOperator != null) {
name += "" + maybeOperator;
}
buf.append(name + "\n");
for (Map.Entry<String, Object> entry : props.entrySet()) {
Object child = entry.getValue();
if (child == null)
continue;
if (IGNORE_PROP_NAMES.contains(entry.getKey()))
continue;
if (child instanceof List && ((List<?>) child).isEmpty())
continue;
buf.append(indent + entry.getKey() + ":");
if (child instanceof EObject) {
buf.append(" " + stringify((EObject) child, indent + INDENT));
} else if (child instanceof List) {
List<?> list = (List<?>) child;
buf.append("\n");
for (Object kid : list) {
buf.append(indent + INDENT);
buf.append(stringify(kid, indent + INDENT + INDENT));
}
} else {
buf.append(" " + child + "\n");
}
}
return buf.toString().replace("Impl", "");
}
/**
* Why do I have to write this function??!
*/
public static Map<String, Object> getProperties(Object obj) {
Map<String, Object> props = new HashMap<String, Object>();
for (Method m : obj.getClass().getMethods()) {
int mod = m.getModifiers();
if (Modifier.isPublic(mod) && !Modifier.isStatic(mod)
&& m.getParameterTypes().length == 0
&& m.getName().startsWith("get")) {
String name = m.getName().substring(3, 4).toLowerCase()
+ m.getName().substring(4);
Object value = null;
try {
value = m.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
props.put(name, value);
}
}
return props;
}
/**
* Given a class with static int fields, get the field name for an id
*/
@SuppressWarnings("rawtypes")
public static String getFieldNameForId(Class clazz, int id) {
for (Field f : clazz.getFields()) {
try {
Object fieldValue = f.get(null);
if (fieldValue.equals(id)) {
String name = f.getName();
// We are interested only in rule names
if (name.startsWith("RULE_"))
return name;
else
continue;
}
} catch (Exception e) {
// ignore
}
}
return null;
}
/**
* Get only the token name and text
*/
public static String getNameAndText(Token token) {
if (token == null) {
return null;
} else {
String value = token.getText();
String name = Helper.getFieldNameForId(
InternalCoffeeScriptLexer.class, token.getType());
if (name == null)
name = "<" + token.getType() + ">";
if (name.startsWith("RULE_"))
name = name.substring("RULE_".length());
if (token.getType() == InternalCoffeeScriptLexer.RULE_TERMINATOR)
value = "";
return name + ":" + value;
}
}
/**
* Check whether an indented block should start after <var>line</var>
*
* @param line
* @return if it's a special line
*/
public static boolean isBlockContainer(String line) {
String raw = line.trim();
if (raw.endsWith("=")
|| raw.endsWith(":")
|| raw.endsWith("->")
|| raw.endsWith("=>"))
return true;
if (raw.startsWith("if ")
|| raw.startsWith("unless ")
|| raw.startsWith("else if ")
|| raw.startsWith("when ")) {
return !raw.contains(" then ");
}
if (raw.startsWith("for ")
|| raw.startsWith("while ")
|| raw.startsWith("class ")
|| raw.startsWith("catch ")
|| raw.startsWith("switch "))
return true;
if (raw.equals("else")
|| raw.equals("try")
|| raw.equals("finally")
|| raw.equals("catch"))
return true;
return false;
}
/**
* Check if the start location of successive tokens are increasing
* @param tokens
* @return the index where the assumption fails, or -1 when start locations are increasing
*/
public static int checkTokenStarts(List<CoffeeSymbol> tokens) {
int problemIndex = -1;
for (int i = 1; i < tokens.size(); i++) {
CoffeeSymbol prev = tokens.get(i-1);
CoffeeSymbol current = tokens.get(i);
//System.out.println(current.debugString());
if (prev.getStart() > current.getStart()) {
problemIndex = i;
System.out.println("\t\t!!! at " + prev + ", " + current);
//throw new RuntimeException("bad " + tokens);
}
}
return problemIndex;
//System.out.println("---\n");
}
public static boolean checkTokenOverflow(List<CoffeeSymbol> tokens, int maxOffset) {
return tokens.get(tokens.size() - 1).getEnd() < maxOffset;
}
}