package xapi.dev.source;
import xapi.source.read.JavaLexer;
import xapi.source.read.JavaVisitor.TypeData;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
public abstract class MemberBuffer<Self extends MemberBuffer<Self>> extends
PrintBuffer implements CanAddImports {
protected final Set<String> annotations;
protected final Set<String> generics;
protected final String origIndent;
protected PrintBuffer javaDoc;
protected int modifier = Modifier.PUBLIC;
public MemberBuffer() {
this("");
}
protected MemberBuffer(StringBuilder target) {
super(target);
origIndent = indent;
annotations = new TreeSet<String>();
generics = new TreeSet<String>();
}
public MemberBuffer(final String indent) {
this.indent = indent;
origIndent = indent;
annotations = new TreeSet<String>();
generics = new TreeSet<String>();
}
@SuppressWarnings("unchecked")
public final Self self() {
return (Self) this;
}
public final Self addGenerics(final String... generics) {
for (String generic : generics) {
generic = generic.trim();
final boolean noImport = generic.contains("!");
if (noImport) {
this.generics.add(generic.replace("!", ""));
} else {
// pull fqcn into import statements and shorten them
this.generics.add(getImports().importFullyQualifiedNames(generic));
}
}
return self();
}
public abstract String addImport(String cls);
public abstract String addImport(Class<?> cls);
public abstract String addImportStatic(String cls);
public abstract String addImportStatic(Class<?> cls, String name);
public abstract String addImportStatic(String cls, String name);
public final Self addImports(final String... clses) {
for (final String cls : clses) {
addImport(cls);
}
return self();
}
public final Self addImports(final Class<?>... clses) {
for (final Class<?> cls : clses) {
addImport(cls);
}
return self();
}
public final Self addAnnotation(final Class<?> anno) {
this.annotations.add(addImport(anno));
return self();
}
public final Self addAnnotation(String anno) {
if (anno.charAt(0) == '@') {
anno = anno.substring(1);
}
anno = anno.trim();// never trust user input :)
final int openParen = anno.indexOf('(');
if (openParen == -1) {
final int hasPeriod = anno.lastIndexOf('.');
if (hasPeriod != -1) {
// fqcn is the whole string.
anno = addImport(anno);
}
} else {
// Need to check fqcn for imports
final int hasPeriod = anno.lastIndexOf('.', openParen);
if (hasPeriod != -1) {
// fqcn is the whole string.
final String annoName = addImport(anno.substring(0, openParen));
anno = annoName + anno.substring(openParen);
}
}
this.annotations.add(anno);
return self();
}
protected final PrintBuffer createJavadoc() {
if (javaDoc == null) {
javaDoc = new PrintBuffer();
}
return javaDoc;
}
public final Self setJavadoc(final String doc) {
final String[] bits = doc.split("\n");
if (bits.length > 0) {
javaDoc = new PrintBuffer();
javaDoc.indent = origIndent;
if (bits.length == 1) {
javaDoc.println("/** " + doc + " */");
} else {
javaDoc.println("/**");
for (final String bit : bits) {
javaDoc.print("* ");
if ("".equals(bit)) {
javaDoc.println("<br/>");
} else {
javaDoc.println(bit);
}
}
javaDoc.println("*/");
}
}
return self();
}
public final Self makeFinal() {
if ((modifier & Modifier.ABSTRACT) > 0)
{
modifier &= ~Modifier.ABSTRACT;// "Cannot be both final and abstract";
}
modifier = modifier | Modifier.FINAL;
return self();
}
/**
TODO: StatementBuffers.
enum StatementType {
IF, ELSE, ELSE_IF, BINARY, ADD, SUB, MULT, DIV, TRY, CATCH, FINALLY, etc.
}
public StatementBuffer makeStatement(StatementType type) {}
*/
protected Self makeAbstract() {
if ((modifier & Modifier.FINAL) > 0)
{
modifier &= ~Modifier.FINAL;// "Cannot be both final and abstract";
}
if ((modifier & Modifier.STATIC) > 0)
{
modifier &= ~Modifier.STATIC;// "Cannot be both static and abstract";
}
modifier = modifier | Modifier.ABSTRACT;
return self();
}
public final Self makeStatic() {
if ((modifier & Modifier.ABSTRACT) > 0)
{
modifier &= ~Modifier.ABSTRACT; // "Cannot be both static and abstract";
}
modifier = modifier | Modifier.STATIC;
return self();
}
public final Self makeConcrete() {
modifier = modifier & ~Modifier.ABSTRACT;
return self();
}
public final Self makePublic() {
modifier = (modifier & 0xFFF8) + Modifier.PUBLIC;
return self();
}
public final Self makeProtected() {
modifier = (modifier & 0xFFF8) + Modifier.PROTECTED;
return self();
}
public final Self makePrivate() {
modifier = (modifier & 0xFFF8) + Modifier.PRIVATE;
return self();
}
public final Self makePackageProtected() {
modifier = modifier & 0xFFF8;
return self();
}
public boolean isStatic() {
return (modifier & Modifier.STATIC) > 0;
}
public boolean isFinal() {
return (modifier & Modifier.FINAL) > 0;
}
protected boolean isAbstract() {
return (modifier & Modifier.ABSTRACT) > 0;
}
public Self setModifier(final int modifier) {
if ((modifier & 7) > 0) {
switch (modifier & 7) {
case Modifier.PUBLIC:
makePublic();
break;
case Modifier.PRIVATE:
makePrivate();
break;
case Modifier.PROTECTED:
makeProtected();
break;
}
}
this.modifier |= modifier;
return self();
}
@Override
public final Self append(final boolean b) {
super.append(b);
return self();
}
@Override
public final Self append(final char c) {
super.append(c);
return self();
}
@Override
public final Self append(final char[] str) {
super.append(str);
return self();
}
@Override
public final Self append(final char[] str, final int offset, final int len) {
super.append(str, offset, len);
return self();
}
@Override
public final Self append(final CharSequence s) {
super.append(s);
return self();
}
@Override
public final Self append(final CharSequence s, final int start, final int end) {
super.append(s, start, end);
return self();
}
@Override
public final Self append(final double d) {
super.append(d);
return self();
}
@Override
public final Self append(final float f) {
super.append(f);
return self();
}
@Override
public final Self append(final int i) {
super.append(i);
return self();
}
@Override
public final Self append(final long lng) {
super.append(lng);
return self();
}
@Override
public final Self append(final Object obj) {
super.append(obj);
return self();
}
@Override
public final Self append(final String str) {
super.append(str);
return self();
}
@Override
public final Self indent() {
super.indent();
return self();
}
@Override
public final Self indentln(final char[] str) {
super.indentln(str);
return self();
}
@Override
public final Self indentln(final CharSequence s) {
super.indentln(s);
return self();
}
@Override
public final Self indentln(final Object obj) {
super.indentln(obj);
return self();
}
@Override
public final Self indentln(final String str) {
super.indentln(str);
return self();
}
@Override
public final Self outdent() {
super.outdent();
return self();
}
@Override
public final Self println() {
super.println();
return self();
}
@Override
public final Self println(final char[] str) {
super.println(str);
return self();
}
@Override
public final Self println(final CharSequence s) {
super.println(s);
return self();
}
@Override
public final Self println(final Object obj) {
super.println(obj);
return self();
}
@Override
public final Self println(final String str) {
super.println(str);
return self();
}
@Override
public final Self printlns(final String str) {
super.printlns(str);
return self();
}
@Override
public Self print(final String str) {
super.print(str);
return self();
}
protected void addNamedTypes(final Set<String> result,
final Iterable<Entry<String, Class<?>>> types) {
for (final Entry<String, Class<?>> type : types) {
final String shortName = addImport(type.getValue());
result.add(shortName + " " + type.getKey());
}
}
protected void addNamedTypes(final Set<String> result, final String... types) {
for (String parameter : types) {
parameter = parameter.trim();
final int index = parameter.lastIndexOf(' ');
assert index > 0 : "Malformed named parameter missing ' ': " + parameter
+ "; from " + Arrays.asList(types);
final TypeData type = JavaLexer.extractType(parameter, 0);
final String shortName = addImport(type.getImportName());
result.add(shortName + " " + parameter.substring(index + 1).trim());
}
}
protected void addTypes(final Set<String> result, final Class<?>... types) {
for (final Class<?> type : types) {
result.add(addImport(type.getCanonicalName()));
}
}
protected void addTypes(final Set<String> result, final String... types) {
for (String typeName : types) {
typeName = typeName.trim();
final TypeData type = JavaLexer.extractType(typeName, 0);
if (type.pkgName.length() > 0) {
final String shortName = addImport(type.getImportName());
result.add(shortName);
}
result.add(typeName);
}
}
@Override
public String coerce(Object obj) {
if (obj instanceof Class) {
return addImport((Class) obj);
} else if (obj instanceof Enum){
final Enum e = (Enum) obj;
// Important: .getClass() will return the enum instance's class, which may actually be
// a subclass of the enum type, if that enum has a body:
// enum Type {
// Type,
// Subtype { void overrides(); }
// ;
// void overrides(){}
// }
String cls = addImport(e.getDeclaringClass());
return cls+"."+e.name();
}
return super.coerce(obj);
}
@Override
public Self makeChild() {
return (Self) super.makeChild();
}
@Override
public Self add(Object... values) {
super.add(values);
return self();
}
}