package org.cellocad.adaptors.ucfadaptor;
import lombok.Getter;
import lombok.Setter;
import org.apache.log4j.Logger;
import org.cellocad.MIT.dnacompiler.Args;
import org.cellocad.MIT.dnacompiler.Gate;
import org.cellocad.MIT.dnacompiler.GateLibrary;
import org.cellocad.MIT.dnacompiler.UCF;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* Created by Bryan Der on 7/29/15.
*/
public class UCFValidator {
//validateResponseFunctions only checks to see if each individual response_function object has the required fields.
//it does not check to see that all gates have a response function.
public boolean allGatesHaveResponseFunctions(GateLibrary gate_library) {
for (Gate g : gate_library.get_GATES_BY_NAME().values()) {
if(g.get_equation().isEmpty() || g.get_params().isEmpty() || g.get_variable_names().isEmpty()) {
System.out.println("invalid UCF. response_function data missing for " + g.Name);
return false;
}
}
return true;
}
//validateGateParts only checks to see if each individual gate_parts object has the required fields,
//it does not check to see that each gate object has gate_parts defined.
public boolean allGatesHaveGateParts(GateLibrary gate_library) {
for(Gate g: gate_library.get_GATES_BY_NAME().values()) {
if(g.get_downstream_parts().isEmpty()) {
return false;
}
if( g.get_regulable_promoter().get_name().length() == 0) {
return false;
}
}
return true;
}
//Validate that each object in each collection has the required fields.
public JSONObject validateAllUCFCollections(UCF ucf, Args options) {
JSONObject ucf_validation_map = new JSONObject();
ucf_validation_map.put("is_valid", true);
ucf_validation_map.put("header", validateHeader(ucf.get_header()));
ucf_validation_map.put("measurement_std", validateMeasurementStd(ucf.get_measurement_std()));
ucf_validation_map.put("logic_constraints", validateLogicConstraints(ucf.get_logic_constraints()));
ucf_validation_map.put("gates", validateGates(ucf.get_gates()));
ucf_validation_map.put("parts", validateParts(ucf.get_parts()));
ucf_validation_map.put("response_functions", validateResponseFunctions(ucf.get_response_functions(), options));
ucf_validation_map.put("gate_parts", validateGateParts(ucf.get_gate_parts()));
ucf_validation_map.put("gate_toxicity", validateGateToxicity(ucf.get_gate_toxicity()));
ucf_validation_map.put("gate_cytometry", validateGateCytometry(ucf.get_gate_cytometry()));
ucf_validation_map.put("eugene_rules", validateEugeneRules(ucf.get_eugene_rules()));
ucf_validation_map.put("genetic_locations", validateGeneticLocations(ucf.get_genetic_locations()));
ucf_validation_map.put("tandem_promoters", validateTandemPromoters(ucf.get_tandem_promoters()));
if( (boolean) ucf_validation_map.get("header") == false ||
(boolean) ucf_validation_map.get("measurement_std") == false ||
(boolean) ucf_validation_map.get("logic_constraints") == false ||
(boolean) ucf_validation_map.get("gates") == false ||
(boolean) ucf_validation_map.get("parts") == false ||
(boolean) ucf_validation_map.get("gate_parts") == false ||
(boolean) ucf_validation_map.get("response_functions") == false
) {
ucf_validation_map.put("is_valid", false);
}
if( ! (boolean) ucf_validation_map.get("gate_toxicity")) {
options.set_toxicity(false);
}
if( ! (boolean) ucf_validation_map.get("gate_cytometry")) {
options.set_histogram(false);
}
if( ! (boolean) ucf_validation_map.get("genetic_locations")) {
options.set_plasmid(false);
}
if( ! (boolean) ucf_validation_map.get("tandem_promoters")) {
options.set_tandem_promoter(false);
}
return ucf_validation_map;
}
public boolean fieldIsMissing(JSONObject obj, List<String> required_fields) {
for(String field: required_fields) {
if(!obj.containsKey(field)) {
System.out.println("missing field: " + field);
return true;
}
}
return false;
}
public boolean validateHeader(JSONObject map) {
if(map.isEmpty()) {
return false;
}
List<String> required_fields = new ArrayList<String>();
required_fields.add("version");
required_fields.add("organism");
required_fields.add("genome");
required_fields.add("media");
required_fields.add("temperature");
required_fields.add("growth");
if(fieldIsMissing(map, required_fields)) {
return false;
}
logger.info("header is valid");
return true;
}
public boolean validateMeasurementStd(JSONObject map) {
if(map.isEmpty()) {
return false;
}
List<String> required_fields = new ArrayList<String>();
required_fields.add("signal_carrier_units");
required_fields.add("normalization_instructions");
required_fields.add("plasmid_description");
required_fields.add("plasmid_sequence");
if(fieldIsMissing(map, required_fields)) {
return false;
}
logger.info("measurement_std is valid");
return true;
}
public boolean validateLogicConstraints(JSONObject map) {
if(map.isEmpty()) {
return false;
}
if(!map.containsKey("available_gates")) {
return false;
}
try {
JSONArray available_gates = (JSONArray) map.get("available_gates");
for (int i=0; i < available_gates.size(); i++) {
JSONObject obj = (JSONObject) available_gates.get(i);
List<String> required_fields = new ArrayList<String>();
required_fields.add("type");
required_fields.add("max_instances");
if(fieldIsMissing(obj, required_fields)) {
return false;
}
}
}catch(Exception e) {
return false;
}
logger.info("logic_constraints are valid");
return true;
}
public boolean validateGates(JSONArray jsonArray) {
if(jsonArray.isEmpty()) {
return false;
}
for (int i=0; i < jsonArray.size(); i++) {
JSONObject obj = (JSONObject) jsonArray.get(i);
List<String> required_fields = new ArrayList<String>();
required_fields.add("regulator");
required_fields.add("group_name");
required_fields.add("gate_name");
required_fields.add("gate_type");
required_fields.add("system");
//optional: color_hexcode
//optional: inducer (this was added for an IWBDA demo, not really used in Cello).
if(fieldIsMissing(obj, required_fields)) {
return false;
}
Pattern p = Pattern.compile("[^a-zA-Z0-9_]");
boolean hasSpecialChar = p.matcher(obj.get("gate_name").toString()).find();
if(hasSpecialChar) {
return false;
}
}
logger.info("gates are valid");
return true;
}
public boolean validateResponseFunctions(JSONArray jsonArray, Args options) {
if(jsonArray.size() == 0) {
return false;
}
for (int i=0; i < jsonArray.size(); i++) {
JSONObject obj = (JSONObject) jsonArray.get(i);
List<String> required_fields = new ArrayList<String>();
required_fields.add("gate_name");
required_fields.add("equation");
required_fields.add("variables");
required_fields.add("parameters");
if(fieldIsMissing(obj, required_fields)) {
return false;
}
JSONArray variables = (JSONArray) obj.get("variables");
if(variables.size() == 0) {
return false;
}
for (int j=0; j < variables.size(); j++) {
JSONObject v = (JSONObject) variables.get(j);
if(!v.containsKey("name")) {
return false;
}
if(!v.containsKey("off_threshold")) {
options.set_noise_margin(false);
}
else {
try {
Double d = Double.valueOf(v.get("off_threshold").toString());
}catch (Exception e) {
return false;
}
}
if(!v.containsKey("on_threshold")) {
options.set_noise_margin(false);
}
else {
try {
Double d = Double.valueOf(v.get("on_threshold").toString());
}catch (Exception e) {
return false;
}
}
}
JSONArray parameters = (JSONArray) obj.get("parameters");
if(parameters.size() == 0) {
return false;
}
for (int j=0; j < parameters.size(); j++) {
JSONObject p = (JSONObject) parameters.get(j);
if(!p.containsKey("name")) {
return false;
}
if(!p.containsKey("value")) {
return false;
}
try {
Double d = Double.valueOf(p.get("value").toString());
}catch (Exception e) {
return false;
}
}
}
logger.info("response_functions are valid");
return true;
}
public boolean validateParts(JSONArray jsonArray) {
if(jsonArray.isEmpty()) {
return false;
}
for (int i=0; i < jsonArray.size(); i++) {
JSONObject obj = (JSONObject) jsonArray.get(i);
List<String> required_fields = new ArrayList<String>();
required_fields.add("type");
required_fields.add("name");
required_fields.add("dnasequence");
if(fieldIsMissing(obj, required_fields)) {
return false;
}
}
logger.info("parts are valid");
return true;
}
public boolean validateGateParts(JSONArray jsonArray) {
if(jsonArray.isEmpty()) {
return false;
}
for (int i=0; i < jsonArray.size(); i++) {
JSONObject obj = (JSONObject) jsonArray.get(i);
List<String> required_fields = new ArrayList<String>();
required_fields.add("gate_name");
required_fields.add("expression_cassettes");
required_fields.add("promoter");
if (fieldIsMissing(obj, required_fields)) {
return false;
}
JSONArray expression_cassettes = (JSONArray) obj.get("expression_cassettes");
for (int j = 0; j < expression_cassettes.size(); j++) {
JSONObject x = (JSONObject) expression_cassettes.get(j);
if (!x.containsKey("maps_to_variable")) {
return false;
}
if (!x.containsKey("cassette_parts")) {
return false;
}
}
}
logger.info("gate_parts are valid");
return true;
}
public boolean validateGateToxicity(JSONArray jsonArray) {
if(jsonArray.isEmpty()) {
return false;
}
for (int i=0; i < jsonArray.size(); i++) {
JSONObject obj = (JSONObject) jsonArray.get(i);
List<String> required_fields = new ArrayList<String>();
required_fields.add("gate_name");
required_fields.add("input");
required_fields.add("growth");
if (fieldIsMissing(obj, required_fields)) {
return false;
}
JSONArray input_vals = (JSONArray) obj.get("input");
for (int j=0; j < input_vals.size(); j++) {
try {
Double.valueOf(input_vals.get(j).toString());
}
catch (Exception e) {
return false;
}
}
JSONArray growth_vals = (JSONArray) obj.get("growth");
for (int j=0; j < input_vals.size(); j++) {
try {
Double.valueOf(growth_vals.get(j).toString());
}
catch (Exception e) {
return false;
}
}
}
return true;
}
public boolean validateGateCytometry(JSONArray jsonArray) {
if(jsonArray.isEmpty()) {
return false;
}
for (int i=0; i < jsonArray.size(); i++) {
JSONObject obj = (JSONObject) jsonArray.get(i);
List<String> required_fields = new ArrayList<String>();
required_fields.add("gate_name");
required_fields.add("cytometry_data");
if (fieldIsMissing(obj, required_fields)) {
return false;
}
JSONArray cytometry_data = (JSONArray) obj.get("cytometry_data");
for (int j=0; j < cytometry_data.size(); j++) {
JSONObject x = (JSONObject) cytometry_data.get(j);
if (!x.containsKey("input")) {
return false;
}
if (!x.containsKey("output_bins")) {
return false;
}
if (!x.containsKey("output_counts")) {
return false;
}
try {
Double.valueOf(x.get("input").toString());
}
catch (Exception e) {
return false;
}
JSONArray output_bins = (JSONArray) x.get("output_bins");
JSONArray output_counts = (JSONArray) x.get("output_counts");
if(output_bins.size() != output_counts.size()) {
return false;
}
for (int k=0; k < output_bins.size(); k++) {
try {
Double.valueOf(output_bins.get(k).toString());
}
catch (Exception e) {
return false;
}
}
for (int k=0; k < output_counts.size(); k++) {
try {
Double.valueOf(output_counts.get(k).toString());
}
catch (Exception e) {
return false;
}
}
}
}
return true;
}
public boolean validateEugeneRules(JSONObject map) {
if(map.isEmpty()) {
return false;
}
List<String> required_fields = new ArrayList<String>();
required_fields.add("eugene_gate_rules");
required_fields.add("eugene_part_rules");
if(fieldIsMissing(map, required_fields)) {
return false;
}
JSONArray gate_rules = (JSONArray) map.get("eugene_gate_rules");
JSONArray part_rules = (JSONArray) map.get("eugene_part_rules");
ArrayList<String> keywords = new ArrayList<String>();
//counting
keywords.add("CONTAINS");
keywords.add("NOTCONTAINS");
keywords.add("EXACTLY");
keywords.add("NOTEXACTLY");
keywords.add("MORETHAN");
keywords.add("NOTMORETHAN");
keywords.add("SAME_COUNT");
keywords.add("WITH");
keywords.add("NOTWITH");
keywords.add("THEN");
//positioning
keywords.add("STARTSWITH");
keywords.add("ENDSWITH");
keywords.add("AFTER");
keywords.add("ALL_AFTER");
keywords.add("SOME_AFTER");
keywords.add("BEFORE");
keywords.add("ALL_BEFORE");
keywords.add("SOME_BEFORE");
keywords.add("NEXTTO");
keywords.add("ALL_NEXTTO");
keywords.add("SOME_NEXTTO");
//pairing
keywords.add("EQUALS");
keywords.add("NOTEQUALS");
//orientation
keywords.add("ALL_FORWARD");
keywords.add("ALL_REVERSE");
keywords.add("FORWARD");
keywords.add("ALL_FORWARD");
keywords.add("REVERSE");
keywords.add("ALL_REVERSE");
keywords.add("SAME_ORIENTATION");
keywords.add("ALL_SAME_ORIENTATION");
keywords.add("ALTERNATE_ORIENTATION");
//interaction
keywords.add("REPRESSES");
keywords.add("INDUCES");
keywords.add("DRIVES");
//logic
keywords.add("NOT");
keywords.add("AND");
keywords.add("OR");
ArrayList<String> all_rules = new ArrayList<String>();
all_rules.addAll(gate_rules);
all_rules.addAll(part_rules);
for(int i=0; i<all_rules.size(); ++i) {
String[] tokens = all_rules.get(i).toString().split(" ");
for(int j=0; j<tokens.length; ++j) {
if(keywords.contains(tokens[j])) {
return true;
}
}
return false;
}
return true;
}
public boolean validateGeneticLocations(JSONObject map) {
/*List<String> required_fields = new ArrayList<String>();
required_fields.add("locations");
required_fields.add("sensor_module_location");
required_fields.add("circuit_module_location");
required_fields.add("output_module_location");
if(fieldIsMissing(map, required_fields)) {
return false;
}*/
if(map.containsKey("locations")) {
JSONArray locations = (JSONArray) map.get("locations");
for (int i = 0; i < locations.size(); ++i) {
JSONObject obj = (JSONObject) locations.get(i);
if (!obj.containsKey("name")) {
return false;
}
if (!obj.containsKey("file")) {
return false;
}
}
}
if(map.containsKey("sensor_module_location")) {
JSONArray locations = (JSONArray) map.get("sensor_module_location");
for (int i = 0; i < locations.size(); ++i) {
JSONObject obj = (JSONObject) locations.get(i);
if (!obj.containsKey("location_name")) {
return false;
}
if (!obj.containsKey("bp_start") || !obj.containsKey("bp_end")){
return false;
}
}
}
if(map.containsKey("circuit_module_location")) {
JSONArray locations = (JSONArray) map.get("sensor_module_location");
for (int i = 0; i < locations.size(); ++i) {
JSONObject obj = (JSONObject) locations.get(i);
if (!obj.containsKey("location_name")) {
return false;
}
if (!obj.containsKey("bp_start") || !obj.containsKey("bp_end")){
return false;
}
}
}
if(map.containsKey("output_module_location")) {
JSONArray locations = (JSONArray) map.get("sensor_module_location");
for (int i = 0; i < locations.size(); ++i) {
JSONObject obj = (JSONObject) locations.get(i);
if (!obj.containsKey("location_name")) {
return false;
}
if (!obj.containsKey("bp_start") || !obj.containsKey("bp_end")){
return false;
}
}
}
return true;
}
public boolean validateTandemPromoters(JSONArray jsonArray) {
if(jsonArray.isEmpty()) {
return false;
}
for (int i=0; i < jsonArray.size(); i++) {
JSONObject obj = (JSONObject) jsonArray.get(i);
List<String> required_fields = new ArrayList<String>();
required_fields.add("name");
required_fields.add("gateA");
required_fields.add("gateB");
required_fields.add("0x_equation");
required_fields.add("1x_equation");
required_fields.add("x0_equation");
required_fields.add("x1_equation");
required_fields.add("0x_params");
required_fields.add("1x_params");
required_fields.add("x0_params");
required_fields.add("x1_params");
if (fieldIsMissing(obj, required_fields)) {
return false;
}
}
return true;
}
@Getter
@Setter
private String threadDependentLoggername;
private Logger logger = Logger.getLogger(getClass());
}