package net.ptnkjke.jbeditor.gui.main.constantpanes.table;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import net.ptnkjke.jbeditor.logic.Core;
import net.ptnkjke.jbeditor.logic.bcel.bytecode.CellConstantWorker;
import net.ptnkjke.jbeditor.logic.own.bytecode.ConstantPool;
import net.ptnkjke.jbeditor.logic.own.bytecode.OClass;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* TODO: Интересные невероятности. BCEL не имеет возможности удалнеия константы, нет возможности контроирования пулов....
* Нельзя взять просто так и отредактриовать сущесвующую.. всё это происходид через дополнительные методы.
*/
/**
* Created by Lopatin on 09.07.2014.
*/
public class Controller {
@FXML
private TableView<ConstantTableCell> table;
@FXML
private TextArea cpEditor;
private ClassGen classGen;
private OClass oClass;
@FXML
public void initialize() {
TableColumn<ConstantTableCell, Integer> column = (TableColumn<ConstantTableCell, Integer>) table.getColumns().get(0);
column.setCellValueFactory(new PropertyValueFactory<ConstantTableCell, Integer>("id"));
TableColumn<ConstantTableCell, String> column1 = (TableColumn<ConstantTableCell, String>) table.getColumns().get(1);
column1.setCellValueFactory(new PropertyValueFactory<ConstantTableCell, String>("type"));
TableColumn<ConstantTableCell, String> column2 = (TableColumn<ConstantTableCell, String>) table.getColumns().get(2);
column2.setCellValueFactory(new PropertyValueFactory<ConstantTableCell, String>("value"));
table.setEditable(true);
// Добавляем поле, которое будет появляться для редактирования
column2.setCellFactory(param -> new EditingCell());
// Автосохранение изменений
column2.setOnEditCommit(
t -> {
t.getRowValue().setValue(t.getNewValue());
t.getRowValue().setChanged(true);
/* ((ConstantTableCell) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setValue(t.getNewValue());*/
});
}
public void setClassGen(ClassGen cg) {
this.classGen = cg;
ConstantPoolGen cpg = cg.getConstantPool();
int length = cpg.getConstantPool().getLength();
for (int i = 1; i < length; i++) {
Constant constant = cpg.getConstant(i);
if (constant == null) {
continue;
}
ConstantTableCell ctb;
CellConstantWorker cellConstantWorker = new CellConstantWorker(cpg.getConstantPool());
try {
cellConstantWorker.visit(constant);
} catch (Exception e) {
e.printStackTrace();
}
ctb = cellConstantWorker.getConstantTableCell();
if (ctb == null) {
continue;
}
ctb.setConst_type(constant.getTag());
ctb.setId(i);
table.getItems().add(ctb);
}
// Преобразуем константу в текст
// TODO: А нахуя этот кусок?
/* StringBuilder sb = new StringBuilder();
for (int i = 1; i < length; i++) {
Constant constant = cpg.getConstant(i);
if (constant == null) {
continue;
}
TextConstantWorker textConstantWorker = new TextConstantWorker(cpg.getConstantPool());
try {
textConstantWorker.visit(constant);
} catch (Exception e) {
e.printStackTrace();
}
sb.append(textConstantWorker.getText()).append("\n");
}*/
oClass = new OClass();
try {
oClass.readFromBytes(Core.INSTANCE.getClassMap().get(cg.getClassName()));
} catch (Exception e) {
e.printStackTrace();
}
cpEditor.setText(ConstantPool.getConstantPoolCode(oClass.getConstantPool()));
}
// Сохранение констант
public void saveConstants() throws Exception {
ConstantPoolGen constantPoolGen =
classGen.getConstantPool();
for (ConstantTableCell cell : table.getItems()) {
if (!cell.isChanged()) {
continue;
}
int old_id = cell.getId();
int new_id = -1;
// Получаем новый id-шник
switch (cell.getConst_type()) {
case Constants.CONSTANT_Utf8:
new_id = constantPoolGen.addUtf8(cell.getValue());
break;
case Constants.CONSTANT_Integer:
new_id = constantPoolGen.addInteger(Integer.parseInt(cell.getValue()));
break;
case Constants.CONSTANT_Float:
new_id = constantPoolGen.addFloat(Float.parseFloat(cell.getValue()));
break;
case Constants.CONSTANT_Long:
new_id = constantPoolGen.addLong(Integer.parseInt(cell.getValue()));
break;
case Constants.CONSTANT_Double:
new_id = constantPoolGen.addDouble(Double.parseDouble(cell.getValue()));
break;
case Constants.CONSTANT_Class:
// TODO:
break;
case Constants.CONSTANT_Fieldref:
// TODO:
break;
case Constants.CONSTANT_String:
new_id = constantPoolGen.addString(cell.getValue());
break;
case Constants.CONSTANT_Methodref:
// TODO:
break;
case Constants.CONSTANT_InterfaceMethodref:
// TODO:
break;
case Constants.CONSTANT_NameAndType:
// TODO:
break;
case Constants.CONSTANT_MethodHandle:
// TODO:
break;
case Constants.CONSTANT_MethodType:
// TODO:
break;
default:
throw new Exception("");
}
List<Method> changes = new ArrayList<>();
// Обхоим все инструкции и меняем страый id-шник на новый
for (int i = 0; i < classGen.getMethods().length; i++) {
MethodGen methodGen = new MethodGen(classGen.getMethodAt(i), classGen.getClassName(), classGen.getConstantPool());
InstructionHandle handle = methodGen.getInstructionList().getStart();
do {
Instruction instr = handle.getInstruction();
if (instr instanceof CPInstruction) {
CPInstruction cpInstruction = (CPInstruction) instr;
if (cpInstruction.getIndex() == old_id) {
cpInstruction.setIndex(new_id);
}
}
handle = handle.getNext();
} while (handle != null);
changes.add(methodGen.getMethod());
}
// Вставляем обновлённые методы обратно и радуемся
classGen.setMethods(changes.toArray(new Method[changes.size()]));
// Update bytes in Core
Core.INSTANCE.getClassMap().put(classGen.getClassName(), classGen.getJavaClass().getBytes());
}
}
// Сохранение констант из текстового редактора
public void saveConstantsFromEditor() {
String code = cpEditor.getText();
oClass.setConstantPool(ConstantPool.parseCode(code));
// Update bytes in Core
try {
Core.INSTANCE.getClassMap().put(classGen.getClassName(), oClass.dump());
} catch (IOException e) {
e.printStackTrace();
}
}
// Приватный класс для представление в таблице
class EditingCell extends TableCell<ConstantTableCell, String> {
private TextField textField;
public EditingCell() {
}
@Override
public void startEdit() {
super.startEdit();
/* if (textField == null) {*/
createTextField();
/* }*/
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.selectAll();
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}