package pt.ist.fenixframework.dml;
import java.io.PrintWriter;
import java.util.Iterator;
import pt.ist.fenixframework.FenixFramework;
import pt.ist.fenixframework.adt.bplustree.IBPlusTree;
import pt.ist.fenixframework.indexes.InitializerBPlusTree;
/**
* This code generator enhances the default generation by adding indexation to fields
* annotated to have that behavior. To do so, it:
* <ul>
*
* <li>Changes setters to update the index (and initializes the index tree if needed)</li>
*
* <li>Adds a static method to allow an index search by the field</li>
*
* </ul>
* @author nmld
*/
public class IndexesCodeGenerator extends TxIntrospectorCodeGenerator {
public static final String FENIX_FRAMEWORK_FULL_CLASS = FenixFramework.class.getName();
/** Cannot refer directly to the BPlusTree.class because that would load the class into the VM, and thus load the base class.
* That is a problem because this class (the CodeGenerator) is loaded when passed to the DmlCompiler. And only after that, will
* the base class ever be generated. Thus we have a cyclic dependency and must break it by only using the BPlusTree name. */
public static final String BPLUS_TREE_FULL_CLASS = "pt.ist.fenixframework.adt.bplustree.BPlusTree";
public static final String INTERFACE_BPLUS_TREE_FULL_CLASS = IBPlusTree.class.getName();
public static final String INITIALIZER_BPLUS_TREE_FULL_CLASS = InitializerBPlusTree.class.getName();
public IndexesCodeGenerator(CompilerArgs compArgs, DomainModel domainModel) {
super(compArgs, domainModel);
}
@Override
protected void generateBaseClassBody(DomainClass domClass, PrintWriter out) {
super.generateBaseClassBody(domClass, out);
generateIndexMethods(domClass, out);
}
@Override
protected void generateSetterBody(DomainClass domainClass, String setterName, Slot slot, PrintWriter out) {
generateIndexationInSetter(domainClass, slot, out);
super.generateSetterBody(domainClass, setterName, slot, out);
}
protected void generateIndexationInSetter(DomainClass domainClass, Slot slot, PrintWriter out) {
if (!slot.hasIndexedAnnotation()) {
return;
}
// Check if the previous field was null. If not, remove it from the index.
boolean slotMayBeNull = mayBeNull(slot.getSlotType());
if (slotMayBeNull) {
print(out, "if (");
print(out, "get" + capitalize(slot.getName() + "()"));
print(out, " != null)");
newBlock(out);
}
print(out, getStaticFieldName(slot.getName()));
print(out, ".remove(");
print(out, "get" + capitalize(slot.getName() + "()"));
print(out, ");");
if (slotMayBeNull) {
closeBlock(out);
}
// Check if the new field value is null. If not, add it to the index.
if (slotMayBeNull) {
print(out, "if (");
print(out, slot.getName());
print(out, " != null)");
newBlock(out);
}
onNewline(out);
print(out, getStaticFieldName(slot.getName()));
print(out, ".insert(");
print(out, slot.getName());
print(out, ", (");
print(out, domainClass.getFullName());
print(out, ")this);");
if (slotMayBeNull) {
closeBlock(out);
}
onNewline(out);
}
private static final String[] primitiveTypes = { "byte", "short", "int", "long", "float", "double", "boolean", "char" };
private boolean mayBeNull(ValueType slotType) {
String name = slotType.getFullname();
for (String primitiveType : primitiveTypes) {
if (primitiveType.equals(name)) {
return false;
}
}
return true;
}
protected String getIndexedFieldKey(String fullDomainClassName, String slotName) {
return "\"" + fullDomainClassName + "." + slotName + "\"";
}
protected void generateIndexMethods(DomainClass domainClass, PrintWriter out) {
Iterator<Slot> slotsIter = domainClass.getSlots();
while (slotsIter.hasNext()) {
generateSlotSearchIndex(domainClass, (Slot) slotsIter.next(), out);
}
}
protected void generateSlotSearchIndex(DomainClass domainClass, Slot slot, PrintWriter out) {
if (slot.hasIndexedAnnotation()) {
generateStaticIndexField(domainClass, slot, out);
generateStaticIndexMethod(domainClass, slot, out);
}
}
private void generateStaticIndexField(DomainClass domainClass, Slot slot, PrintWriter out) {
newline(out);
print(out, "public static transient ");
print(out, INTERFACE_BPLUS_TREE_FULL_CLASS);
print(out, "<");
print(out, domainClass.getFullName());
print(out, ">");
print(out, " ");
print(out, getStaticFieldName(slot.getName()));
print(out, " = new ");
print(out, INITIALIZER_BPLUS_TREE_FULL_CLASS);
print(out, "<");
print(out, domainClass.getFullName());
print(out, ">");
print(out, "(");
print(out, getIndexedFieldKey(domainClass.getFullName(), slot.getName()));
print(out, ", ");
print(out, domainClass.getBaseName());
print(out, ".class, \"");
print(out, getStaticFieldName(slot.getName()));
print(out, "\");");
newline(out);
}
private String getStaticFieldName(String slotName) {
return slotName + "$index";
}
protected void generateStaticIndexMethod(DomainClass domainClass, Slot slot, PrintWriter out) {
newline(out);
printFinalMethod(out, "public static", domainClass.getFullName(), getStaticIndexMethodName(slot), makeArg(slot.getTypeName(), slot.getName()));
startMethodBody(out);
generateStaticIndexMethodBody(domainClass.getFullName(), slot, out);
endMethodBody(out);
}
protected void generateStaticIndexMethodBody(String fullDomainClassName, Slot slot, PrintWriter out) {
// Generate the search
print(out, "return ");
print(out, getStaticFieldName(slot.getName()));
print(out, ".get(");
print(out, slot.getName());
print(out, ");");
}
protected String getStaticIndexMethodName(Slot slotName) {
return "findBy" + slotName.getName().substring(0, 1).toUpperCase() + slotName.getName().substring(1);
}
}