package blue.stack.serializableParcelablegenerator;
import blue.stack.serializableParcelablegenerator.typeserializers.*;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import java.util.List;
public class CodeGenerator {
// public static final String CREATOR_NAME = "CREATOR";
private final PsiClass mClass;
private final List<PsiField> mFields;
private final TypeSerializerFactory mTypeSerializerFactory;
public CodeGenerator(PsiClass psiClass, List<PsiField> fields) {
mClass = psiClass;
mFields = fields;
this.mTypeSerializerFactory = new ChainSerializerFactory(
new BundleSerializerFactory(),
new DateSerializerFactory(),
new EnumerationSerializerFactory(),
new ParcelableListSerializerFactory(),
new PrimitiveTypeSerializerFactory(),
new PrimitiveArraySerializerFactory(),
new ListSerializerFactory(),
new ParcelableSerializerFactory(),
new SerializableSerializerFactory()
);
}
private String generateStaticCreator(PsiClass psiClass) {
// StringBuilder sb = new StringBuilder("public static final blue.stack.serializableParcelable.IParcelable.Creator<");
//
// String className = psiClass.getName();
//
// sb.append(className).append("> CREATOR = new blue.stack.serializableParcelable.IParcelable.Creator<").append(className).append(">(){")
StringBuilder sb = new StringBuilder("");
sb.append("@Override public ").append(" byte").append("[] getParacelableBytes() {")
.append("return new ").append("byte").append("[0];}")
;
return sb.toString();
}
// @Override
//public void createFromParcel(IParcel in)
private String generateConstructor(List<PsiField> fields, PsiClass psiClass) {
String className = psiClass.getName();
StringBuilder sb = new StringBuilder("@Override public blue.stack.serializableParcelable.IParcelable createFromParcel(blue.stack.serializableParcelable.IParcel in) {");
// Creates all of the deserialization methods for the given fields
for (PsiField field : fields) {
sb.append(getSerializerForType(field).readValue(field, "in"));
}
sb.append("\n" +
"return this;");
sb.append("}");
return sb.toString();
}
private String generateWriteToParcel(List<PsiField> fields) {
StringBuilder sb = new StringBuilder("@Override public void writeToParcel(blue.stack.serializableParcelable.IParcel dest, int constructID) {");
sb.append("dest.writeInt(constructID);\n");
for (PsiField field : fields) {
sb.append(getSerializerForType(field).writeValue(field, "dest", "constructID"));
}
sb.append("}");
return sb.toString();
}
private String generategetParacelableBytes() {
StringBuilder sb = new StringBuilder("@Override public byte[] getParacelableBytes() {");
// ParcelObject parcelObject = new ParcelObject();
// writeToParcel(parcelObject,constructID);
// return parcelObject.toByteArray();
sb.append("\n blue.stack.serializableParcelable.ParcelObject parcelObject = new blue.stack.serializableParcelable.ParcelObject(); \n");
sb.append("writeToParcel(parcelObject,constructID); \n");
sb.append("return parcelObject.toByteArray(); \n");
sb.append("}");
return sb.toString();
}
private TypeSerializer getSerializerForType(PsiField field) {
return mTypeSerializerFactory.getSerializer(field.getType());
}
// private String generateDescribeContents() {
// return "@Override public int describeContents() { return 0; }";
// }
public void generate() {
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(mClass.getProject());
removeExistingParcelableImplementation(mClass);
// Describe contents method
// PsiMethod describeContentsMethod = elementFactory.createMethodFromText(generateDescribeContents(), mClass);
// Method for writing to the parcel
PsiMethod writeToParcelMethod = elementFactory.createMethodFromText(generateWriteToParcel(mFields), mClass);
// Default constructor if needed
String defaultConstructorString = generateDefaultConstructor(mClass);
PsiMethod defaultConstructor = null;
PsiMethod generategetParacelableBytesMethod = elementFactory.createMethodFromText(generategetParacelableBytes(), mClass);
if (defaultConstructorString != null) {
defaultConstructor = elementFactory.createMethodFromText(defaultConstructorString, mClass);
}
// Constructor
PsiMethod constructor = elementFactory.createMethodFromText(generateConstructor(mFields, mClass), mClass);
// CREATOR
// PsiField creatorField = elementFactory.createFieldFromText(generateStaticCreator(mClass), mClass);
JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(mClass.getProject());
// Shorten all class references
// styleManager.shortenClassReferences(mClass.addBefore(describeContentsMethod, mClass.getLastChild()));
styleManager.shortenClassReferences(mClass.addBefore(writeToParcelMethod, mClass.getLastChild()));
styleManager.shortenClassReferences(mClass.addBefore(generategetParacelableBytesMethod, mClass.getLastChild()));
// Only adds if available
if (defaultConstructor != null) {
styleManager.shortenClassReferences(mClass.addBefore(defaultConstructor, mClass.getLastChild()));
}
styleManager.shortenClassReferences(mClass.addBefore(constructor, mClass.getLastChild()));
// styleManager.shortenClassReferences(mClass.addBefore(creatorField, mClass.getLastChild()));
makeClassImplementParcelable(elementFactory);
}
/**
* Strips the
*
* @param psiClass
*/
private void removeExistingParcelableImplementation(PsiClass psiClass) {
PsiField[] allFields = psiClass.getAllFields();
// Look for an existing CREATOR and remove it
// for (PsiField field : allFields) {
// if (field.getName().equals(CREATOR_NAME)) {
// // Creator already exists, need to remove/replace it
// field.delete();
// }
// }
findAndRemoveMethod(psiClass, "createFromParcel", "blue.stack.serializableParcelable.IParcel");
findAndRemoveMethod(psiClass, "getParacelableBytes");
// findAndRemoveMethod(psiClass, "describeContents");
findAndRemoveMethod(psiClass, "writeToParcel", "blue.stack.serializableParcelable.IParcel", "int");
}
private String generateDefaultConstructor(PsiClass clazz) {
// Check for any constructors; if none exist, we'll make a default one
if (clazz.getConstructors().length == 0) {
// No constructors exist, make a default one for convenience
StringBuilder sb = new StringBuilder();
sb.append("public ").append(clazz.getName()).append("(){}").append('\n');
return sb.toString();
} else {
return null;
}
}
private void makeClassImplementParcelable(PsiElementFactory elementFactory) {
final PsiClassType[] implementsListTypes = mClass.getImplementsListTypes();
final String implementsType = "blue.stack.serializableParcelable.IParcelable";
for (PsiClassType implementsListType : implementsListTypes) {
PsiClass resolved = implementsListType.resolve();
// Already implements Parcelable, no need to add it
if (resolved != null && implementsType.equals(resolved.getQualifiedName())) {
return;
}
}
PsiJavaCodeReferenceElement implementsReference = elementFactory.createReferenceFromText(implementsType, mClass);
PsiReferenceList implementsList = mClass.getImplementsList();
if (implementsList != null) {
implementsList.add(implementsReference);
}
}
private static void findAndRemoveMethod(PsiClass clazz, String methodName, String... arguments) {
// Maybe there's an easier way to do this with mClass.findMethodBySignature(), but I'm not an expert on Psi*
PsiMethod[] methods = clazz.findMethodsByName(methodName, false);
for (PsiMethod method : methods) {
PsiParameterList parameterList = method.getParameterList();
if (parameterList.getParametersCount() == arguments.length) {
boolean shouldDelete = true;
PsiParameter[] parameters = parameterList.getParameters();
for (int i = 0; i < arguments.length; i++) {
if (!parameters[i].getType().getCanonicalText().equals(arguments[i])) {
shouldDelete = false;
}
}
if (shouldDelete) {
method.delete();
}
}
}
}
}