package com.constellio.app.modules.rm.services.reports;
import static com.constellio.app.ui.i18n.i18n.$;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.ALL;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import static java.util.Arrays.asList;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.query.JRXPathQueryExecuterFactory;
import net.sf.jasperreports.engine.util.JRXmlUtils;
import org.apache.commons.lang.NullArgumentException;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.joda.time.LocalDate;
import com.constellio.app.modules.rm.services.RMSchemasRecordsServices;
import com.constellio.app.modules.rm.services.decommissioning.DecommissioningService;
import com.constellio.app.modules.rm.wrappers.ContainerRecord;
import com.constellio.app.modules.rm.wrappers.Folder;
import com.constellio.app.services.factories.AppLayerFactory;
import com.constellio.model.entities.EnumWithSmallCode;
import com.constellio.model.entities.records.Content;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.schemas.Metadata;
import com.constellio.model.entities.schemas.MetadataSchemaType;
import com.constellio.model.entities.schemas.MetadataValueType;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.entities.security.Role;
import com.constellio.model.services.contents.ContentManager;
import com.constellio.model.services.contents.ContentVersionDataSummary;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.search.query.logical.LogicalSearchQuery;
import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition;
import com.constellio.model.services.users.UserServices;
/**
* Created by Nicolas D'Amours & Charles Blanchette on 2017-01-16.
*/
public class ReportUtils {
public static final boolean DEV = true;
private RMSchemasRecordsServices rm;
private SearchServices ss;
private RecordServices recordServices;
private int startingPosition, numberOfCopies;
private ContentManager contentManager;
private AppLayerFactory factory;
private String collection;
private String usr;
private UserServices userServices;
private List<DataField> otherDataForContainer;
public ReportUtils(String collection, AppLayerFactory appLayerFactory, String usr) {
this.factory = appLayerFactory;
this.collection = collection;
this.rm = new RMSchemasRecordsServices(collection, factory);
this.ss = factory.getModelLayerFactory().newSearchServices();
this.recordServices = factory.getModelLayerFactory().newRecordServices();
this.startingPosition = 0;
this.contentManager = factory.getModelLayerFactory().getContentManager();
this.numberOfCopies = 1;
this.usr = usr;
this.userServices = factory.getModelLayerFactory().newUserServices();
this.otherDataForContainer = getotherDataForContainer();
JasperReportsContext jasperReportsContext = DefaultJasperReportsContext.getInstance();
jasperReportsContext.setProperty("net.sf.jasperreports.awt.ignore.missing.font", "true");
jasperReportsContext.setProperty("net.sf.jasperreports.default.font.name", "Arial");
}
private List<DataField> getotherDataForContainer() {
List<DataField> data = new ArrayList<>();
data.addAll(asList(
new DataField("dispositionDate", DecommissioningService.class).setMethod("getDispositionDate", ContainerRecord.class).setInstance(new DecommissioningService(this.collection, this.factory)),
new DataField("decommissioningLabel", DecommissioningService.class).setMethod("getDecommissionningLabel", ContainerRecord.class).setInstance(new DecommissioningService(this.collection, this.factory))
));
return data;
}
/**
* Cette méthode prend une liste de métadonnées en paramètre.
* On cherche tous les dossiers d'une collection et crée un XML à partir
* des métadonnées de ces fichiers.
*
* @param parameters
* @return String - Le document XML
* @throws Exception
*/
public String convertFolderToXML(ReportField... parameters) throws Exception {
LogicalSearchCondition condition = from(rm.folder.schemaType()).where(ALL);
List<Folder> foldersFound = rm.wrapFolders(ss.search(new LogicalSearchQuery(condition)));
Document document = new Document();
Element root = new Element("folders");
document.setRootElement(root);
for (int i = 0; i < this.getStartingPosition(); i++) {
Element container = new Element("folder");
Element metadatas = new Element("metadatas");
container.setContent(metadatas);
root.addContent(container);
}
if (parameters == null) {
List<ReportField> temp = new ArrayList<>();
for (Metadata m : rm.folder.schema().getMetadatas()) {
String schemaType = m.getType().equals(MetadataValueType.REFERENCE) ? m.getReferencedSchemaType() : Folder.SCHEMA_TYPE;
temp.add(new ReportField(m.getType(), m.getCode().split("_")[2], schemaType, m.getCode(), factory));
}
parameters = temp.toArray(new ReportField[0]);
}
for (int i = 0; i < this.numberOfCopies; i++) {
for (Folder fol : foldersFound) {
Element folder = new Element("folder");
Element metadatas = new Element("metadatas");
for (ReportField metadonnee : parameters) {
MetadataSchemaType schema = factory.getModelLayerFactory().getMetadataSchemasManager().getSchemaTypes(this.collection).getSchemaType(metadonnee.getSchema());
// if (!schema.getSchema(schema.getDefaultSchema().getCode()).hasMetadataWithCode(schema.getDefaultSchema().getCode() + "_" + metadonnee.getCode()))
// throw new MetadataException("No such metadata " + metadonnee.getCode() + " for schema " + metadonnee.getSchema());
if (metadonnee.getTypes().equals(MetadataValueType.REFERENCE)) {
// recordServices.getDocumentById()
List<String> IdsList = fol.getSchema().get(metadonnee.getCode()).isMultivalue() ? asList(fol.getList(fol.getSchema().getMetadata(metadonnee.getCode())).toArray(new String[0])) : asList((String) fol.get(fol.getSchema().getMetadata(metadonnee.getCode())));
List<Record> referenceRecords = recordServices.getRecordsById(this.collection, IdsList);
for (Record refRecords : referenceRecords) {
Element refElementCode = new Element("ref_" + metadonnee.getCode().replace("_default", "") + "_code");
refElementCode.setText(refRecords.get(Schemas.CODE) + "");
Element refElementTitle = new Element("ref_" + metadonnee.getCode().replace("_default", "") + "_title");
refElementTitle.setText(refRecords.get(Schemas.TITLE) + "");
metadatas.addContent(asList(refElementCode, refElementTitle));
}
} else if (metadonnee.getTypes().equals(MetadataValueType.ENUM)) {
if (fol.get(metadonnee.getCode()) != null) {
Element refElementCode = new Element(escapeForXmlTag(metadonnee.getLabel()) + "_code");
refElementCode.setText(((EnumWithSmallCode) fol.get(metadonnee.getCode())).getCode());
Element refElementTitle = new Element(escapeForXmlTag(metadonnee.getLabel()) + "_title");
refElementTitle.setText(fol.get(metadonnee.getCode()) + "");
metadatas.addContent(asList(refElementCode, refElementTitle));
}
} else {
Element m = new Element(escapeForXmlTag(metadonnee.getLabel()));
m.setText(metadonnee.formatData(fol.get(metadonnee.getCode()) != null ? fol.get(metadonnee.getCode()) + "" : null));
metadatas.addContent(m);
}
}
folder.setContent(metadatas);
root.addContent(folder);
}
}
XMLOutputter xmlOutputter = new XMLOutputter(DEV ? Format.getPrettyFormat() : Format.getCompactFormat());
return xmlOutputter.outputString(document);
}
/**
* Cette méthode prend une liste de métadonnées en paramètre.
* On cherche le dossier correspondant au id dans la collection et crée un XML à partir
* des métadonnées de ce dossier.
*
* @param parameters
* @return String - Le document XML
* @throws Exception
*/
public String convertFolderWithIdentifierToXML(String id, ReportField... parameters) throws Exception {
if (id == null) throw new NullArgumentException("The id is null !");
return convertFolderWithIdentifierToXML(asList(id), parameters);
}
/**
* Cette méthode prend une liste de métadonnées en paramètre.
* On cherche les dossiers correspondant aux ids dans la collection et crée un XML à partir
* des métadonnées de ces dossiers.
*
* @param parameters
* @return String - Le document XML
* @throws Exception
*/
public String convertFolderWithIdentifierToXML(List<String> ids, ReportField... parameters) throws Exception {
if (ids == null) throw new NullArgumentException("The ids list is null !");
LogicalSearchCondition condition = from(rm.folder.schemaType()).where(Schemas.IDENTIFIER).isIn(ids);
List<Folder> foldersFound = rm.wrapFolders(ss.search(new LogicalSearchQuery(condition)));
Document document = new Document();
Element root = new Element("folders");
document.setRootElement(root);
for (int i = 0; i < this.getStartingPosition(); i++) {
Element container = new Element("folder");
Element metadatas = new Element("metadatas");
container.setContent(metadatas);
root.addContent(container);
}
if (parameters == null) {
List<ReportField> temp = new ArrayList<>();
for (Metadata m : rm.folder.schema().getMetadatas()) {
String schemaType = m.getType().equals(MetadataValueType.REFERENCE) ? m.getReferencedSchemaType() : Folder.SCHEMA_TYPE;
temp.add(new ReportField(m.getType(), m.getCode().split("_")[2], schemaType, m.getCode(), factory));
}
parameters = temp.toArray(new ReportField[0]);
}
for (int i = 0; i < this.numberOfCopies; i++) {
for (Folder fol : foldersFound) {
Element folder = new Element("folder");
Element metadatas = new Element("metadatas");
for (ReportField reportField : parameters) {
MetadataSchemaType schema = factory.getModelLayerFactory().getMetadataSchemasManager().getSchemaTypes(this.collection).getSchemaType(reportField.getSchema());
// if (!schema.getSchema(schema.getDefaultSchema().getCode()).hasMetadataWithCode(schema.getDefaultSchema().getCode() + "_" + metadonnee.getCode()))
// throw new MetadataException("No such metadata " + metadonnee.getCode() + " for schema " + metadonnee.getSchema());
if (reportField.getTypes().equals(MetadataValueType.REFERENCE)) {
// recordServices.getDocumentById()
List<String> IdsList = fol.getSchema().get(reportField.getCode()).isMultivalue() ? asList(fol.getList(fol.getSchema().getMetadata(reportField.getCode())).toArray(new String[0])) : asList((String) fol.get(fol.getSchema().getMetadata(reportField.getCode())));
List<Record> referenceRecords = recordServices.getRecordsById(this.collection, IdsList);
for (Record refRecords : referenceRecords) {
Element refElementCode = new Element("ref_" + reportField.getCode().replace("_default", "") + "_code");
refElementCode.setText(refRecords.get(Schemas.CODE) + "");
Element refElementTitle = new Element("ref_" + reportField.getCode().replace("_default", "") + "_title");
refElementTitle.setText(refRecords.get(Schemas.TITLE) + "");
metadatas.addContent(asList(refElementCode, refElementTitle));
}
} else if (reportField.getTypes().equals(MetadataValueType.ENUM)) {
if (fol.get(reportField.getCode()) != null) {
Element refElementCode = new Element(escapeForXmlTag(reportField.getLabel()) + "_code");
refElementCode.setText(((EnumWithSmallCode) fol.get(reportField.getCode())).getCode());
Element refElementTitle = new Element(escapeForXmlTag(reportField.getLabel()) + "_title");
refElementTitle.setText(fol.get(reportField.getCode()) + "");
metadatas.addContent(asList(refElementCode, refElementTitle));
}
} else {
Element m = new Element(reportField.getLabel() != null ? escapeForXmlTag(reportField.getLabel()) : reportField.getCode().split("_")[2]);
m.setText(reportField.formatData(fol.get(reportField.getCode()) != null ? fol.get(reportField.getCode()) + "" : null));
metadatas.addContent(m);
}
}
folder.setContent(metadatas);
root.addContent(folder);
}
}
XMLOutputter xmlOutputter = new XMLOutputter(DEV ? Format.getPrettyFormat() : Format.getCompactFormat());
return xmlOutputter.outputString(document);
}
/**
* Cette méthode prend une liste de métadonnées en paramètre.
* On cherche tous les conteneurs d'une collection et crée un XML à partir
* des métadonnées de ces fichiers.
*
* @param parameters
* @return String - Le document XML
* @throws Exception
*/
public String convertContainerToXML(ReportField... parameters) throws Exception {
LogicalSearchCondition condition = from(rm.containerRecord.schemaType()).where(ALL);
List<ContainerRecord> containersFound = rm.wrapContainerRecords(ss.search(new LogicalSearchQuery(condition).filteredWithUser(this.userServices.getUserInCollection(this.usr, this.collection), Role.READ)));
Document document = new Document();
Element root = new Element("containers");
document.setRootElement(root);
for (int i = 0; i < this.getStartingPosition(); i++) {
Element container = new Element("container");
Element metadatas = new Element("metadatas");
container.setContent(metadatas);
root.addContent(container);
}
if (parameters == null) {
List<ReportField> temp = new ArrayList<>();
for (Metadata m : rm.containerRecord.schema().getMetadatas()) {
String schemaType = m.getType().equals(MetadataValueType.REFERENCE) ? m.getReferencedSchemaType() : ContainerRecord.SCHEMA_TYPE;
temp.add(new ReportField(m.getType(), m.getCode().split("_")[2], schemaType, m.getCode(), factory));
}
parameters = temp.toArray(new ReportField[0]);
}
for (int i = 0; i < this.numberOfCopies; i++) {
for (ContainerRecord con : containersFound) {
Element container = new Element("container");
Element metadatas = new Element("metadatas");
for (ReportField metadonnee : parameters) {
MetadataSchemaType schema = factory.getModelLayerFactory().getMetadataSchemasManager().getSchemaTypes(this.collection).getSchemaType(metadonnee.getSchema());
// if (!schema.getSchema(schema.getDefaultSchema().getCode()).hasMetadataWithCode(schema.getDefaultSchema().getCode() + "_" + metadonnee.getCode()))
// throw new MetadataException("No such metadata " + metadonnee.getCode() + " for schema " + metadonnee.getSchema());
if (metadonnee.getTypes().equals(MetadataValueType.REFERENCE)) {
// recordServices.getDocumentById()
List<String> IdsList = con.getSchema().get(metadonnee.getCode()).isMultivalue() ? asList(con.getList(con.getSchema().getMetadata(metadonnee.getCode())).toArray(new String[0])) : asList((String) con.get(con.getSchema().getMetadata(metadonnee.getCode())));
List<Record> referenceRecords = recordServices.getRecordsById(this.collection, IdsList);
for (Record refRecords : referenceRecords) {
Element refElementCode = new Element("ref_" + metadonnee.getCode().replace("_default", "") + "_code");
refElementCode.setText(refRecords.get(Schemas.CODE) + "");
Element refElementTitle = new Element("ref_" + metadonnee.getCode().replace("_default", "") + "_title");
refElementTitle.setText(refRecords.get(Schemas.TITLE) + "");
metadatas.addContent(asList(refElementCode, refElementTitle));
}
} else if (metadonnee.getTypes().equals(MetadataValueType.ENUM)) {
if (con.get(metadonnee.getCode()) != null) {
Element refElementCode = new Element(escapeForXmlTag(metadonnee.getLabel()) + "_code");
refElementCode.setText(((EnumWithSmallCode) con.get(metadonnee.getCode())).getCode());
Element refElementTitle = new Element(escapeForXmlTag(metadonnee.getLabel()) + "_title");
refElementTitle.setText(con.get(metadonnee.getCode()) + "");
metadatas.addContent(asList(refElementCode, refElementTitle));
}
} else {
Element m = new Element(escapeForXmlTag(metadonnee.getLabel()));
m.setText(metadonnee.formatData(con.get(metadonnee.getCode()) != null ? con.get(metadonnee.getCode()) + "" : null));
metadatas.addContent(m);
}
}
for (DataField dataField : this.otherDataForContainer) {
Element e = new Element(dataField.getKey());
String value = dataField.calculate(new Object[]{con}) + "";
e.setText(value);
metadatas.addContent(e);
}
container.setContent(metadatas);
root.addContent(container);
}
}
XMLOutputter xmlOutputter = new XMLOutputter(DEV ? Format.getPrettyFormat() : Format.getCompactFormat());
return xmlOutputter.outputString(document);
}
/**
* Cette méthode prend une liste de métadonnées en paramètre.
* On cherche le conteneur correspondant au id dans la collection et crée un XML à partir
* des métadonnées de ce contenant.
*
* @param parameters
* @return String - Le document XML
* @throws Exception
*/
public String convertContainerWithIdentifierToXML(String id, ReportField... parameters) throws Exception {
if (id == null) throw new NullArgumentException("The id is null !");
return convertContainerWithIdentifierToXML(asList(id), parameters);
}
/**
* Cette méthode prend une liste de métadonnées en paramètre.
* On cherche les conteneurs correspondants aux ids dans la collection et crée un XML à partir
* des métadonnées de ces contenants.
*
* @param parameters
* @return String - Le document XML
* @throws Exception
*/
public String convertContainerWithIdentifierToXML(List<String> ids, ReportField... parameters) throws Exception {
if (ids == null) throw new NullArgumentException($("listNull"));
LogicalSearchCondition condition = from(rm.containerRecord.schemaType()).where(Schemas.IDENTIFIER).isIn(ids);
List<ContainerRecord> containersFound = rm.wrapContainerRecords(ss.search(new LogicalSearchQuery(condition).filteredWithUser(this.userServices.getUserInCollection(this.usr, this.collection), Role.READ)));
Document document = new Document();
Element root = new Element("containers");
document.setRootElement(root);
for (int i = 0; i < this.getStartingPosition(); i++) {
Element container = new Element("container");
Element metadatas = new Element("metadatas");
container.setContent(metadatas);
root.addContent(container);
}
if (parameters == null) {
List<ReportField> temp = new ArrayList<>();
for (Metadata m : rm.containerRecord.schema().getMetadatas()) {
String schemaType = m.getType().equals(MetadataValueType.REFERENCE) ? m.getReferencedSchemaType() : ContainerRecord.SCHEMA_TYPE;
temp.add(new ReportField(m.getType(), m.getCode().split("_")[2], schemaType, m.getCode(), factory));
}
parameters = temp.toArray(new ReportField[0]);
}
for (int i = 0; i < this.numberOfCopies; i++) {
for (ContainerRecord con : containersFound) {
Element container = new Element("container");
Element metadatas = new Element("metadatas");
for (ReportField metadonnee : parameters) {
MetadataSchemaType schema = factory.getModelLayerFactory().getMetadataSchemasManager().getSchemaTypes(this.collection).getSchemaType(metadonnee.getSchema());
// if (!schema.getSchema(schema.getDefaultSchema().getCode()).hasMetadataWithCode(schema.getDefaultSchema().getCode() + "_" + metadonnee.getCode()))
// throw new MetadataException("No such metadata " + metadonnee.getCode() + " for schema " + metadonnee.getSchema());
if (metadonnee.getTypes().equals(MetadataValueType.REFERENCE)) {
// recordServices.getDocumentById()
List<String> IdsList = con.getSchema().get(metadonnee.getCode()).isMultivalue() ? asList(con.getList(con.getSchema().getMetadata(metadonnee.getCode())).toArray(new String[0])) : asList((String) con.get(con.getSchema().getMetadata(metadonnee.getCode())));
List<Record> referenceRecords = recordServices.getRecordsById(this.collection, IdsList);
for (Record refRecords : referenceRecords) {
Element refElementCode = new Element("ref_" + metadonnee.getCode().replace("_default", "") + "_code");
refElementCode.setText(refRecords.get(Schemas.CODE) + "");
Element refElementTitle = new Element("ref_" + metadonnee.getCode().replace("_default", "") + "_title");
refElementTitle.setText(refRecords.get(Schemas.TITLE) + "");
metadatas.addContent(asList(refElementCode, refElementTitle));
}
} else if (metadonnee.getTypes().equals(MetadataValueType.ENUM)) {
if (con.get(metadonnee.getCode()) != null) {
Element refElementCode = new Element(escapeForXmlTag(metadonnee.getLabel()) + "_code");
refElementCode.setText(((EnumWithSmallCode) con.get(metadonnee.getCode())).getCode());
Element refElementTitle = new Element(escapeForXmlTag(metadonnee.getLabel()) + "_title");
refElementTitle.setText(con.get(metadonnee.getCode()) + "");
metadatas.addContent(asList(refElementCode, refElementTitle));
}
} else {
Element m = new Element(escapeForXmlTag(metadonnee.getLabel()));
m.setText(metadonnee.formatData(con.get(metadonnee.getCode()) != null ? con.get(metadonnee.getCode()) + "" : null));
metadatas.addContent(m);
}
}
for (DataField dataField : this.otherDataForContainer) {
Element e = new Element(dataField.getKey());
String value = dataField.calculate(new Object[]{con}) + "";
e.setText(value);
metadatas.addContent(e);
}
container.setContent(metadatas);
root.addContent(container);
}
}
XMLOutputter xmlOutputter = new XMLOutputter(DEV ? Format.getPrettyFormat() : Format.getCompactFormat());
return xmlOutputter.outputString(document);
}
/**
* Méthode qui prend en paramètre une chaine de caractère sous format XML et un ficher .jasper.
* Il créer ensuite un PDF avec les deux.
*
* @param xml
* @param jasperFile
* @throws Exception
*/
public Content createPDFFromXmlAndJasperFile(String xml, File jasperFile, String format) throws Exception {
long startTime = System.currentTimeMillis();
Map<String, Object> params = new HashMap<>();
org.w3c.dom.Document document = JRXmlUtils.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
params.put(JRXPathQueryExecuterFactory.PARAMETER_XML_DATA_DOCUMENT, document);
// params.put(JRXPathQueryExecuterFactory.P)
String reportFile = JasperFillManager.fillReportToFile(jasperFile.getAbsolutePath(), params);
String PDFFile = JasperExportManager.exportReportToPdfFile(reportFile);
System.err.println("Filling time : " + (System.currentTimeMillis() - startTime));
System.out.println(PDFFile);
// JasperPrint jp = JasperFillManager.fillReport(new FileInputStream(jasperFile), new HashMap<String, Object>(), new JRXmlDataSource());
// JasperExportManager.exportReportToPdfFile(jp, );
File file = new File(PDFFile);
ContentVersionDataSummary upload = contentManager.upload(new FileInputStream(file), "Etiquette");
return contentManager.createFileSystem(escapeForXmlTag(format) + "-" + LocalDate.now(), upload);
}
/**
* Permet de définir à quelle position les étiquettes commencent
* a imprimer.
*
* @param start
*/
public void setStartingPosition(int start) {
this.startingPosition = start;
}
/**
* @return la position de départ pour l'impression.
*/
public int getStartingPosition() {
return this.startingPosition;
}
/**
* Permet de définir le nombre de copies de l'étiquette à
* imprimer
*
* @param nb
*/
public void setNumberOfCopies(int nb) {
this.numberOfCopies = nb;
}
/**
* le nombre de copies choisi.
*
* @return
*/
public int getNumberOfCopies() {
return this.numberOfCopies;
}
/**
* Méthode qui prend en paramètre un String et qui resort le même string seulement il est formater pour être valide dans une balise XML
*
* @param input String
* @return
*/
public static String escapeForXmlTag(String input) {
return input.replace(" ", "_").replaceAll("[éèëê]", "e").replaceAll("[àâáä]", "a").replaceAll("[öòóô]", "o").replace("'", "").replaceAll("-", "_").replaceAll("[üùúû]", "u").replaceAll("[îìíï]", "i").replaceAll("[\\( \\)]", "").replaceAll("[&$%]", "").toLowerCase();
}
private class DataField {
private String key;
private Class clazz;
private String method;
private Object instance;
private Class<?>[] methodParams;
public DataField(String key, Class clazz) {
this.key = key;
this.clazz = clazz;
}
public DataField setMethod(String method, Class<?>... params) {
this.method = method;
this.methodParams = params;
return this;
}
public DataField setInstance(Object instance) {
this.instance = instance;
return this;
}
public String getKey() {
return this.key;
}
public Class getClazz() {
return this.clazz;
}
public String getMethod() {
return this.method;
}
public Object calculate(Object[] parameters) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
return this.clazz.getMethod(this.method, this.methodParams).invoke(this.instance, parameters);
}
}
}